ContentProvider是安卓的四大組件之一,底層使用Binder,可以用於跨進程通信,另外ContentProvider的啓動伴隨着進程的啓動,進程的啓動調用Process的start方法,並且新進程的入口是ActivityThread的main方法。接下來分析ContentProvider如何啓動。
-
ContentProvider的啓動
在main方法中,會調用ActivityManagerService中的attachApplication方法。然後調用attachApplicationLocked方法,在該方法中調用IApplicationThread的bindApplication綁定Application方法,IApplicationThread的Binder對象的實現類是ActivityThread內部的ApplicationThread。
@Override public final void attachApplication(IApplicationThread thread, long startSeq) { synchronized (this) { int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid, callingUid, startSeq); Binder.restoreCallingIdentity(origId); } }
private final boolean attachApplicationLocked(IApplicationThread thread, int pid, int callingUid, long startSeq) { .... thread.bindApplication(processName, appInfo, providers, app.instr.mClass, profilerInfo, app.instr.mArguments, app.instr.mWatcher, app.instr.mUiAutomationConnection, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.persistent, new Configuration(getGlobalConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, isAutofillCompatEnabled); ..... }
ApplicationThread的bindApplication方法,在該方法中會調用 sendMessage(H.BIND_APPLICATION, data) 方法,H是Activity的內部的Handler,最終調用Handler的sendMessage方法。最終調用Handler的handleMessage方法。Handler內部狀態爲BIND_APPLICATION的處理方法。調用handleBindApplication方法
case BIND_APPLICATION: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication"); AppBindData data = (AppBindData)msg.obj; handleBindApplication(data); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break;
handleBindApplication方法,在該方法內部installContentProviders這句代碼就是啓動ContentProvider的方法,啓動之後調用callApplicationOnCreate方法,這句代碼是調用了Application的onCreate方法。所以得出一個結論ContentProvider的啓動在調用Application的onCreate方法之前。
.... // don't bring up providers in restricted mode; they may depend on the // app's custom Application class if (!data.restrictedBackupMode) { if (!ArrayUtils.isEmpty(data.providers)) { installContentProviders(app, data.providers); // 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); } } ..... mInstrumentation.callApplicationOnCreate(app); ......
installContentProviders的方法,在該方法內部調用installProvider方法。接着調用localProvider即ContentProvider方法的attachInfo方法。
rivate ContentProviderHolder installProvider(Context context, ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ... // XXX Need to create the correct context for this provider. localProvider.attachInfo(c, info); ... }
attachInfo方法內部中調用了ContentProvider的onCreate方法,到這裏ContentProvider的啓動就結束了。
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) { 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(); } }
-
ContentProvider的使用分析
ContentProvider是通過getContentResolver()方法獲取ContentResolver調用增刪改查方法。因此使用的分析從增刪改查分析,這裏只以query方法進行分析。getContentResolver方法調用ContextWrapper中的getContentResolver方法,該方法內調用ContextImpl方法的getContentResolver方法。
@Override public ContentResolver getContentResolver() { return mBase.getContentResolver(); }
ContextImpl方法的getContentResolver,最終返回ContextImpl.ApplicationContentResolver對象,該類繼承ContentResolver。
@Override public ContentResolver getContentResolver() { return mContentResolver; }
接下來調用ContentResolver的query方法。最終去調用acquireUnstableProvider或者acquireProvider獲取有用或者無用的IContentProvider這個Binder類型的對象,IContentProvider的具體實現是ContentProviderNative和ContentProvider.Transport,當調用query方法時,實際調用的是ContentProvider.Transport內部的query方法。
public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { return query(uri, projection, selection, selectionArgs, sortOrder, null); }
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) { Preconditions.checkNotNull(uri, "uri"); IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null) { return null; } IContentProvider stableProvider = null; Cursor qCursor = null; try { long startTime = SystemClock.uptimeMillis(); ICancellationSignal remoteCancellationSignal = null; if (cancellationSignal != null) { cancellationSignal.throwIfCanceled(); remoteCancellationSignal = unstableProvider.createCancellationSignal(); cancellationSignal.setRemote(remoteCancellationSignal); } try { qCursor = unstableProvider.query(mPackageName, uri, projection, queryArgs, remoteCancellationSignal); ... }
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; }
ContentProvider.Transport的query方法,最終調用ContentProvider的query方法。
@Override public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) { ... // Null projection means all columns but we have no idea which they are. // However, the caller may be expecting to access them my index. Hence, // we have to execute the query as if allowed to get a cursor with the // columns. We then use the column names to return an empty cursor. Cursor cursor = ContentProvider.this.query( uri, projection, queryArgs, CancellationSignal.fromTransport(cancellationSignal)); if (cursor == null) { return null; } // Return an empty cursor for all columns. return new MatrixCursor(cursor.getColumnNames(), 0); } final String original = setCallingPackage(callingPkg); try { return ContentProvider.this.query( uri, projection, queryArgs, CancellationSignal.fromTransport(cancellationSignal)); } finally { setCallingPackage(original); } }