Android 筆記之 ContentProvider 的工作過程

        ContentProvider 是一種內容型共享組件,通過Binder 向其他組件乃至其他應用提供數據。當ContentProvider 所在的進程啓動時,ContentProvider 會同時啓動並被髮布到AMS 中。注意:此時ContentProvider 的onCreat 方法要先於Application 的onCreat 方法執行。
        當一個應用啓動時,入口方法爲ActivityThread 的main 方法,main 方法是一個靜態方法, 在main 方法中會創建 ActivityThread 的實例並創建主線程的消息隊列,然後在 ActivityThread 的attach 方法中會遠程調用 AMS 的 attachApplication 方法並將 ApplicationThread 對象提供給 AMS。ApplicationThread 是一個Binder 對象,主要用於ActivityThread 和AMS 之間的通信,在AMS 的attachApplication 中,會調用 ApplicationThread 的 bindApplication 方法,這個過程同樣是跨進程完成的,然後經過ActivityThread 的H 切換到 ActivityThread 中執行,具體方法爲 handlerBindApplication 。在handlerBindApplication 方法中,ActivityThread 會創建 Application 對象並加載 ContentProvider。注意:ActivityThread 會先加載ContentProvider ,然後再調用Application 的onCreat 方法。
        ContentProvider 啓動後,外界就可以通過它提供的增刪改查四個接口來操作ContentProvider 中的數據源,這四個方法都是通過Binder 來調用的,外界無法直接訪問ContentProvider ,只能通過AMS 根據Uri 來獲取對應 ContentProvider 的Binder 接口 IContentProvider,然後通過 IContentProvider 來訪問ContentProvider 中的數據源。
    ContentProvider 可有它的 android:multiprocess 屬性來決定是否是單例,當android:multiprocess 爲false 時(默認值),ContentProvider 爲單例,當爲true 時,ContentProvider 爲多實例,這個時候在每個調用者的進程中都存在一個 ContentProvider 對象,但這種場景比較少,因此我們可以簡單的認爲ContentProvider 都是單例。
    訪問ContentProvider 需要通過ContentResolver, ContentResolver 是一個抽象類,它的子類是 ApplicationContentResolver,通過Context 的 getContentResolver 方法實際上獲取的是 ApplicationContentResolver 對象。如下所示:
ContextWrapper:
    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }
ContextImpl
    private final ApplicationContentResolver mContentResolver;

    @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }
    當ContentProvider 所在的進程未啓動時,第一次訪問它時就會觸發ContentProvider 的創建,同時伴隨着 ContentProvider 進程的啓動,通過ContentProvider 的四個方法的任何一個都可以觸發 ContentProvider 的啓動過程,這裏選擇 query 方法說明。
    ContentProvider 的 query 方法中,首先會獲取 IContentProvider 對象,不管是通過acquireUnstableProvider 還是直接通過acquireProvider 方法,它們本質都是通過 acquireProvider 獲取 ContentProvider 對象,如下所示:
    public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable CancellationSignal cancellationSignal) {
        Preconditions.checkNotNull(uri, "uri");
        //實質上還是會調用ApplicationContentResolver 的acquireProvider 獲取 ContentProvider 對象
        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);
            } catch (DeadObjectException e) {
                // The remote process has died...  but we only hold an unstable
                // reference though, so we might recover!!!  Let's try!!!!
                // This is exciting!!1!!1!!!!1
                unstableProviderDied(unstableProvider);
                //獲取 ContentProvider 對象
                stableProvider = acquireProvider(uri);
                if (stableProvider == null) {
                    return null;
                }
                //調用query 方法
                qCursor = stableProvider.query(
                        mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
            }
            if (qCursor == null) {
                return null;
            }

            // Force query execution.  Might fail and throw a runtime exception here.
            qCursor.getCount();
            long durationMillis = SystemClock.uptimeMillis() - startTime;
            maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);

            // Wrap the cursor object into CursorWrapperInner object.
            final IContentProvider provider = (stableProvider != null) ? stableProvider
                    : acquireProvider(uri);
            final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
            stableProvider = null;
            qCursor = null;
            return wrapper;
        } catch (RemoteException e) {
            // Arbitrary and not worth documenting, as Activity
            // Manager will kill this process shortly anyway.
            return null;
        } finally {
            if (qCursor != null) {
                qCursor.close();
            }
            if (cancellationSignal != null) {
                cancellationSignal.setRemote(null);
            }
            if (unstableProvider != null) {
                releaseUnstableProvider(unstableProvider);
            }
            if (stableProvider != null) {
                releaseProvider(stableProvider);
            }
        }
    }
      ApplicationContentResolver 的 acquireProvider 方法如下所示:
    // ----------------------------------------------------------------------
    // ----------------------------------------------------------------------
    // ----------------------------------------------------------------------

    private static final class ApplicationContentResolver extends ContentResolver {
        private final ActivityThread mMainThread;
        private final UserHandle mUser;

        public ApplicationContentResolver(
                Context context, ActivityThread mainThread, UserHandle user) {
            super(context);
            mMainThread = Preconditions.checkNotNull(mainThread);
            mUser = Preconditions.checkNotNull(user);
        }

        @Override
        protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), true);
        }

        @Override
        protected IContentProvider acquireExistingProvider(Context context, String auth) {
            return mMainThread.acquireExistingProvider(context,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), true);
        }

        @Override
        public boolean releaseProvider(IContentProvider provider) {
            return mMainThread.releaseProvider(provider, true);
        }

        @Override
        protected IContentProvider acquireUnstableProvider(Context c, String auth) {
            return mMainThread.acquireProvider(c,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), false);
        }

        @Override
        public boolean releaseUnstableProvider(IContentProvider icp) {
            return mMainThread.releaseProvider(icp, false);
        }

        @Override
        public void unstableProviderDied(IContentProvider icp) {
            mMainThread.handleUnstableProviderDied(icp.asBinder(), true);
        }

        @Override
        public void appNotRespondingViaProvider(IContentProvider icp) {
            mMainThread.appNotRespondingViaProvider(icp.asBinder());
        }

        /** @hide */
        protected int resolveUserIdFromAuthority(String auth) {
            return ContentProvider.getUserIdFromAuthority(auth, mUser.getIdentifier());
        }
    }
    ApplicationContentResolver 的 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;
        }

        // There is a possible race here.  Another thread may try to acquire
        // the same provider at the same time.  When this happens, we want to ensure
        // that the first one wins.
        // Note that we cannot hold the lock while acquiring and installing the
        // provider since it might take a long time to run and it could also potentially
        // be re-entrant in the case where the provider is in the same process.
        IActivityManager.ContentProviderHolder holder = null;
        try {
            //發送請求給 AMS 讓其啓動目標ContentProvider
            holder = ActivityManagerNative.getDefault().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } catch (RemoteException ex) {
        }
        if (holder == null) {
            Slog.e(TAG, "Failed to find provider info for " + auth);
            return null;
        }

        // Install provider will increment the reference count for us, and break
        // any ties in the race.
        //修改計數
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }
    上面的代碼首先從ActivityThread 中會通過 acquireExistingProvider 查找是否已經存在目標 ContentProvider,如果存在就直接返回。ActivityThread 中通過 mProviderMap 來存儲已經啓動的 ContentProvider對象,源碼如下所示:
    // The lock of mProviderMap protects the following variables.
    final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
        = new ArrayMap<ProviderKey, ProviderClientRecord>();

    public final IContentProvider acquireExistingProvider(
            Context c, String auth, int userId, boolean stable) {
        synchronized (mProviderMap) {
            final ProviderKey key = new ProviderKey(auth, userId);
            final ProviderClientRecord pr = mProviderMap.get(key);
            if (pr == null) {
                return null;
            }

            IContentProvider provider = pr.mProvider;
            IBinder jBinder = provider.asBinder();
            if (!jBinder.isBinderAlive()) {
                // The hosting process of the provider has died; we can't
                // use this one.
                Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
                        + ": existing object's process dead");
                handleUnstableProviderDiedLocked(jBinder, true);
                return null;
            }

            // Only increment the ref count if we have one.  If we don't then the
            // provider is not reference counted and never needs to be released.
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                incProviderRefLocked(prc, stable);
            }
            return provider;
        }
    }
    如果當前ContentProvider 沒有啓動,則發送一個進程間請求給 AMS 讓其啓動目標ContentProvider ,最後通過 installProvider 方法來修改計數。
    我們知道ContentProvider 的啓動會伴隨着進程的啓動,在 AMS 中,首先會啓動 ContentProvider 所在的進程,然後再啓動ContentProvider。啓動進程是由AMS 的 startProcessLocked 方法來完成的,其內部主要是通過 Process.start() 方法來完成一個新進程的啓動,新進程啓動後其入口方法爲 ActivityThread 的main 方法,如下所示:
    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        AndroidKeyStoreProvider.install();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");
        //構建mainLooper
        Looper.prepareMainLooper();
        //創建ActivityThread 實例,並調用其attach 方法
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            //獲取關聯的Handler
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        //啓動loop 輪詢
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    從上可看出,ActivityThread 的 main 方法是一個今天方法,在其內部首先會創建 ActivityThread 的實例並調用其 attach 方法來完成一系列初始化,接着就開始進行消息循環。ActivityThread 的 attach 方法會將 ApplicationThread 對象通過 AMS 的 attachApplication 方法跨進程傳遞給 AMS,最終AMS 會完成ContentProvider 的創建過程,源碼如下:
    final ApplicationThread mAppThread = new ApplicationThread();

    private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            ViewRootImpl.addFirstDrawHandler(new Runnable() {
                @Override
                public void run() {
                    ensureJitEnabled();
                }
            });
            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                    UserHandle.myUserId());
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            //AMS
            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }
          ...
    }
    AMS 的 attachApplication 方法調用了 attachApplicationLocked 方法,如下所示:
    @Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }
    在 attachApplicationLocked 中調用了 ApplicationThread 的bindApplication 方法,注意這個過程也是進程間調用如下所示
    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
        ...
            ProfilerInfo profilerInfo = profileFile == null ? null
                    : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
            thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());
            updateLruProcessLocked(app, false, null);
            app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
        ...
        } 

  ActivityThread 的 bindApplication 方法會發送一個 BIND_APPLICATION 類型的消息給 mH(Handler),bindApplication 發送消息的過程如下所示:

        public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
                Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
                Bundle coreSettings) {

            if (services != null) {
                // Setup the service cache in the ServiceManager
                ServiceManager.initServiceCache(services);
            }

            setCoreSettings(coreSettings);

            /*
             * Two possible indications that this package could be
             * sharing its runtime with other packages:
             *
             * 1.) the sharedUserId attribute is set in the manifest,
             *     indicating a request to share a VM with other
             *     packages with the same sharedUserId.
             *
             * 2.) the application element of the manifest has an
             *     attribute specifying a non-default process name,
             *     indicating the desire to run in another packages VM.
             *
             * If sharing is enabled we do not have a unique application
             * in a process and therefore cannot rely on the package
             * name inside the runtime.
             */
            IPackageManager pm = getPackageManager();
            android.content.pm.PackageInfo pi = null;
            try {
                pi = pm.getPackageInfo(appInfo.packageName, 0, UserHandle.myUserId());
            } catch (RemoteException e) {
            }
            if (pi != null) {
                boolean sharedUserIdSet = (pi.sharedUserId != null);
                boolean processNameNotDefault =
                (pi.applicationInfo != null &&
                 !appInfo.packageName.equals(pi.applicationInfo.processName));
                boolean sharable = (sharedUserIdSet || processNameNotDefault);

                // Tell the VMRuntime about the application, unless it is shared
                // inside a process.
                if (!sharable) {
                    VMRuntime.registerAppInfo(appInfo.packageName, appInfo.dataDir,
                                            appInfo.processName);
                }
            }

            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableOpenGlTrace = enableOpenGlTrace;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            sendMessage(H.BIND_APPLICATION, data);
        }

      mH接收到消息後會調用 ActivityThread 的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;

    ActivityThread 的 handleBindApplication 完成了 Application 的創建以及ContentProvider 的創建,可以分爲四個步驟,源碼如下所示:

    private void handleBindApplication(AppBindData data) {
        mBoundApplication = data;
        mConfiguration = new Configuration(data.config);
        mCompatConfiguration = new Configuration(data.config);
            ...
            mInstrumentationPackageName = ii.packageName;
            mInstrumentationAppDir = ii.sourceDir;
            mInstrumentationSplitAppDirs = ii.splitSourceDirs;
            mInstrumentationLibDir = ii.nativeLibraryDir;
            mInstrumentedAppDir = data.info.getAppDir();
            mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
            mInstrumentedLibDir = data.info.getLibDir();

            ApplicationInfo instrApp = new ApplicationInfo();
            instrApp.packageName = ii.packageName;
            instrApp.sourceDir = ii.sourceDir;
            instrApp.publicSourceDir = ii.publicSourceDir;
            instrApp.splitSourceDirs = ii.splitSourceDirs;
            instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs;
            instrApp.dataDir = ii.dataDir;
            instrApp.nativeLibraryDir = ii.nativeLibraryDir;
            LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                    appContext.getClassLoader(), false, true, false);
            /**************************1.start 創建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);
           /**************************1.end 創建ContextImpl 和 Instrumentation*******************/
            if (mProfiler.profileFile != null && !ii.handleProfiling
                    && mProfiler.profileFd == null) {
                mProfiler.handlingProfiling = true;
                File file = new File(mProfiler.profileFile);
                file.getParentFile().mkdirs();
                Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
            }

        } else {
            mInstrumentation = new Instrumentation();
        }

        if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
            dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
        } else {
            // Small heap, clamp to the current growth limit and let the heap release
            // pages after the growth limit to the non growth limit capacity. b/18387825
            dalvik.system.VMRuntime.getRuntime().clampGrowthLimit();
        }

        // Allow disk access during application and provider setup. This could
        // block processing ordered broadcasts, but later processing would
        // probably end up doing the same disk access.
        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
          /**************************2.start Application 對象*******************/
        try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;
          /**************************2.end Application 對象*******************/
            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            
            /**************************3.start 啓動當前進程的ContentProvider 並調用其 onCreat 方法 *******************/
            if (!data.restrictedBackupMode) {
                List<ProviderInfo> providers = data.providers;
                if (providers != null) {
                    installContentProviders(app, 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);
                }
            }
             /**************************3.end 啓動當前進程的ContentProvider 並調用其 onCreat 方法 *******************/
            
            // Do this after providers, since instrumentation tests generally start their
            // test thread at this point, and we don't want that racing.
            /**************************4.start 調用Application的 onCreat 方法 *******************/
            try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            catch (Exception e) {
                throw new RuntimeException(
                    "Exception thrown in onCreate() of "
                    + data.instrumentationName + ": " + e.toString(), e);
            }
           /**************************4.end 調用Application的 onCreat 方法 *******************/
            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);
                }
            }
        } finally {
            StrictMode.setThreadPolicy(savedPolicy);
        }
    }

    installContentProviders 完成了 ContentProvider 的啓動工作,它的實現如下所示:

    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<IActivityManager.ContentProviderHolder> results =
            new ArrayList<IActivityManager.ContentProviderHolder>();

        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(": ");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            ActivityManagerNative.getDefault().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
        }
    }

    首先會遍歷當前進程的 ProviderInfo 列表並一一調用installProvider 方法來啓動它們,接着將已經啓動的 ContentProvider 發佈到AMS 中,AMS 會把它們存儲在 ProviderMap 中,這樣外部調用者就可以直接從AMS 中獲取ContentProvider 了。

    在 installProvider 方法彙總,完成了ContentProvider 對象的創建過程,源碼如下所示:

    private IActivityManager.ContentProviderHolder installProvider(Context context,
            IActivityManager.ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
            ...
            try {
                //通過類加載器完成了ContentProvider 對象的創建
                final java.lang.ClassLoader cl = c.getClassLoader();
                localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
                          info.name + " from sourceDir " +
                          info.applicationInfo.sourceDir);
                    return null;
                }
                if (DEBUG_PROVIDER) Slog.v(
                    TAG, "Instantiating local provider " + info.name);
                // XXX Need to create the correct context for this provider.
                //調用ContentProvider 的attachInfo 方法
                localProvider.attachInfo(c, info);
            } catch (java.lang.Exception e) {
                if (!mInstrumentation.onException(null, e)) {
                    throw new RuntimeException(
                            "Unable to get provider " + info.name
                            + ": " + e.toString(), e);
                }
                return null;
            }
            ...
        }

    在完成了ContentProvider 對象的創建,還會通過ContentProvider 的 attachInfo 方法來調用它的 onCreate 方法,如下所示:

    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 的onCreate 方法
            ContentProvider.this.onCreate();
        }
    }

     至此,ContentProvider 已經被創建並且其 onCreate 方法也已經被調用,意味着ContentProvider 已經啓動完成了。

    經過以上四個步驟,ContentProvider已經成功啓動,且其所在進程AppliCation 也已經啓動,即ContentProvider 所在的進程已經完成了整個的啓動過程,然後其他應用就可以通過AMS 來訪問這個ContentProvider 了。
    注意L這裏的ContentProvider 並不是原始的ContentProvider ,而是ContentProvider 的Binder 對象 IContentProvider,它的具體實現類是ContentProviderNative 和 ContentProvider.Transport,其中ContentProvider.Transport 繼承了 ContentProviderNative 。因此調用ContentProvider 的query 方法 最終會議進程間通信的方式調用到 ContentProvider.Transport 的query 方法,如下所示:
public abstract class ContentProvider implements ComponentCallbacks2 {
    ...
    class Transport extends ContentProviderNative {
        AppOpsManager mAppOpsManager = null;
        int mReadOp = AppOpsManager.OP_NONE;
        int mWriteOp = AppOpsManager.OP_NONE;

        ContentProvider getContentProvider() {
            return ContentProvider.this;
        }

        @Override
        public String getProviderName() {
            return getContentProvider().getClass().getName();
        }

        @Override
        public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
                @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
            validateIncomingUri(uri);
            uri = maybeGetUriWithoutUserId(uri);
            if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                // The caller has no access to the data, so return an empty cursor with
                // the columns in the requested order. The caller may ask for an invalid
                // column and we would not catch that but this is not a problem in practice.
                // We do not call ContentProvider#query with a modified where clause since
                // the implementation is not guaranteed to be backed by a SQL database, hence
                // it may not handle properly the tautology where clause we would have created.
                if (projection != null) {
                    return new MatrixCursor(projection, 0);
                }

                // 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);
            }
        }
       ...
   }
    ...
}
    ContentProvider.Transport 的query 方法調用了 ContentProvider 的query 方法, query 方法的執行結果再通過Binder 返回給調用者,這樣整個調用過程就完成了,其他的inset、delete和update 方法均類似。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章