一、使用ContentProvider示例:
1、在一個單獨的app(B)中定義個MyContentProvider
package com.example.provider;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
public class MyContentProvider extends ContentProvider {
private static final String TAG = "MyContentProvider";
@Override
public boolean onCreate() {
Log.d(TAG,"onCreate");
return false;
}
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
Log.d(TAG,"query , thread name = " + Thread.currentThread().getName());//僅打印個文本,說明調用到了
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
2、在app(B)的AndroidManifest中註冊
<provider
android:authorities="com.example.provider"
android:name="com.example.provider.MyContentProvider"
android:exported="true"
>
</provider>
3、在app(A)中的activity中使用。
模擬了不同應用間獲取數據的場景
Uri uri = Uri.parse("content://com.example.provider");//和XML中的authorities對應
getContentResolver().query(uri,null,null,null,null);
打印如下
2020-04-11 16:48:00.435 30490-30490/? D/MyContentProvider: onCreate
2020-04-11 16:48:11.051 30490-30503/com.example.test D/MyContentProvider: query , thread name = Binder:30490_1
可以看到app(B)中的方法是執行在Binder線程池中的。
注意:
1、當啓動app(B)就會調用ContentProvider的onCreate方法。
2、當沒有啓動app(B)時,在app(A)中調用就會出現異常。提示找不到provider
E/ActivityThread: Failed to find provider info for com.example.provider
所以只有啓動app(B)的進程,才能正常訪問ContentProvider
二、ContentProvider啓動過程
通過Binder調用到AMS(和之前邏輯一樣),再IPC調用到ActivityThread#handleBindApplication
ActivityThread#handleBindApplication
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);//這個裏面就是完成了Provider的創建
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
...
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
mInstrumentation.callApplicationOnCreate(app);//這裏會調用Application的onCreate
ActivityThread#installContentProviders
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
...
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
...
}
ActivityThread#installProvider
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();//看到了吧,通過類加載器加載了Provider的實例
provider = localProvider.getIContentProvider();
...
localProvider.attachInfo(c, info);//接着調用了attachInfo方法
ContentProvider#attachInfo
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
/*
* Only allow it to be set once, so after the content service gives
* this to us clients can't change it.
*/
if (mContext == null) {//默認第一次是null,所以這個只會調用一次
mContext = context;
if (context != null) {
mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
}
mMyUid = Process.myUid();
if (info != null) {
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
setPathPermissions(info.pathPermissions);
mExported = info.exported;
mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
setAuthorities(info.authority);
}
ContentProvider.this.onCreate();//最終調用到了onCreate方法了。
}
}
ContentProvider.this 就是當前的ContentProvider對象
到這裏ContentProvider就隨着app的啓動,加載了。
========上面的代碼是自己分析的。下面是書上總結了ActivityThread#handleBindApplication方法的4個步驟
1.創建ContextImpl和instrumentation
ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
try {
java.lang.ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate instrumentation "
+ data.instrumentationName + ": " + e.toString(), e);
}
mInstrumentation.init(this, instrContext, appContext,
new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
data.instrumentationUiAutomationConnection);
2.創建Application對象
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
3.啓動當前進程的ContentProvider並調用onCreate方法
如何調用到onCreate方法上面已經分析了,這裏不再贅述
4.調用Application的onCreate方法
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
========到這個ContentProvider就啓動好了。如果有其它應用請求這個ContentProvider,AMS就可以直接返回這個對象了。
三、獲取ContentProvider數據過程
ContentProvider的query方法,首先獲取的IContentProvider對象,不管是通過acquireUnstableProvider方法還是直接通過acquireProvider方法,它們的本質都是一樣的,最終都是通過acquireProvider方法來獲取ContentProvider。下面是ApplicationContentResolver的acquireProvider具體的實現方式:
代碼看看是如何獲取到IContentProvider對象的
ContextWrapper#getContentResolver
public ContentResolver getContentResolver() {
return mBase.getContentResolver();//前面分析過了mBase就是ContextImpl
}
ContextImpl#getContentResolver
public ContentResolver getContentResolver() {
return mContentResolver;//private final ApplicationContentResolver mContentResolver;
}
從上面可以知道我們在activity中getContentResolver()拿到的就是一個ApplicationContentResolver對象
ContentResolver#acquireUnstableProvider
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
return acquireUnstableProvider(mContext, uri.getAuthority());//參數個數不一樣,不同的方法。
}
return null;
}
ContextImpl.ApplicationContentResolver#acquireUnstableProvider
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
ApplicationContentResolver的acquireProvider方法並沒有處理任何邏輯,它直接調用了ActivityThread的acquireProvider方法
ActivityThread#acquireProvider
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;//找到了直接返回
}
ContentProviderHolder holder = null;
try {
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);//看到了,如果ActivityThread中沒找到,又通過Binder調用到了AMS中的getContentProvider方法了
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
到這裏就已經可以獲取到IContentProvider這個對象了。
具體的啓動上面已經分析過了(並不知道後面會分析到,之前是自己看源碼分析的。和書上分析一樣。看來有進步了)。
@Override
public Cursor query(String callingPkg, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
ICancellationSignal cancellationSignal) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
}
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.query(
uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
} finally {
setCallingPackage(original);
}
}
到這裏我們4大組件的啓動過程就分析完成了。總結下流程就是ActivityThread 通過ApplicationThread這個Binder類和AMS進行交互的過程。