四大組件-ContentProvider的工作原理基於android9.0

ContentProvider是安卓的四大組件之一,底層使用Binder,可以用於跨進程通信,另外ContentProvider的啓動伴隨着進程的啓動,進程的啓動調用Process的start方法,並且新進程的入口是ActivityThread的main方法。接下來分析ContentProvider如何啓動。

  1. 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();
            }
        }
    
  2. 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);
                }
            }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章