Android-深入底层:深入浅出ContentProvider的启动过程

一、引言

ContentProvider作为Android的四大组件之一,其主要的作用是通过Binder向其他组件或者其他应用提供数据。它的底层实现方式也是Binder,但是它的使用方法又比较简单。原因是系统为我们做了封装,而我们无须关心底层细节就能轻松的实现进程间的通信。

ContentProvider的应用还是挺广泛的,比如我们应用中获取通讯录、获取短信、获取手机通话记录等,都是通过ContentProvider来进行的。

既然ContentProvider这么重要,我们有必要从源码的角度深挖一下ContentProvider的启动过程。

二、源码分析

1、ContentProvider的创建与初始化

在具体分析源码之前,我们大致看一下ContentProvider的整个启动过程的示意图,如下所示:

App启动后,会调用ActivityThread的main方法,接着在main方法中创建了ActivityThread对象,并调用了它的attach方法。在attach方法中远程调用AMS的attachApplication方法,该方法中又远程调用PMS的queryContentProviders方法获取应用注册的Provider信息,然后调用ApplicationThread的bindApplication方法将Provider信息传递过去。在ApplicationThread中通过makeApplication生成Application对象,并调用installContentProviders方法初始化并加载ContentProvider,然后调用Application对象的onCreate方法,应用就这样跑起来了。

下面我们具体分析上述的每一个步骤。

首先是App启动,调用了ActivityThread的main方法,如下所示:

public static void main(String[] args) {
    ...省略

    // 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>");

    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

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

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();
}

main方法中,创建了ActivityThread对象,然后调用了该对象的attach方法,如下所示:

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());
        final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        ...省略
    } else {
        ...省略
    }
    ...省略
}

系统的应用我们不作分析,而对于非系统应用,ActivityManager.getService()获取到一个IActivityManager对象,它是一个Binder对象,通过它调用attachApplication方法实际上就是远程调用了ActivityManagerServiceattachApplication方法,如下所示:

public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid);
        Binder.restoreCallingIdentity(origId);
    }
}

attachApplication方法又调用了attachApplicationLocked方法,如下所示:

private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid) {

    ...省略

    EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);

    app.makeActive(thread, mProcessStats);
    app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
    app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
    app.forcingToImportant = null;
    updateProcessForegroundLocked(app, false, false);
    app.hasShownUi = false;
    app.debugging = false;
    app.cached = false;
    app.killedByAm = false;
    app.killed = false;

    app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);

    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    //获取应用中注册的ContentProvider数据
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;

    if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
        Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
        msg.obj = app;
        mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
    }

    checkTime(startTime, "attachApplicationLocked: before bindApplication");
    try {
        ...省略

        // If we were asked to attach an agent on startup, do so now, before we're binding
        // application code.
        if (agent != null) {
            thread.attachAgent(agent);
        }

        checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
        mStackSupervisor.mActivityMetricsLogger.notifyBindApplication(app);
        if (app.instr != null) {
            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);
        } else {
            thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                    null, null, null, testMode,
                    mBinderTransactionTrackingEnabled, enableTrackAllocation,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(getGlobalConfiguration()), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked(),
                    buildSerial);
        }

        checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
        updateLruProcessLocked(app, false, null);
        checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
        app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
    } catch (Exception e) {
        ...省略
    }

    ...省略

    return true;
}

在attachApplicationLocked方法中,主要流程分为两步:

  • 在正常模式下,调用了generateApplicationProvidersLocked方法获取注册的ContentProvider信息。
  • 将上一步中的ContentProvider信息作为参数,远程调用了ApplicationThread的bindApplication方法。

我们首先看第一步,generateApplicationProvidersLocked方法如下所示:

private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
    List<ProviderInfo> providers = null;
    try {
    	//远程调用PMS获取应用中注册的ContentProvider信息
        providers = AppGlobals.getPackageManager()
                .queryContentProviders(app.processName, app.uid,
                        STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
                                | MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null)
                .getList();
    } catch (RemoteException ex) {
    }
    int userId = app.userId;
    ...省略
    return providers;
}

在上述方法中,AppGlobals.getPackageManager()方法返回的是IPackageManager,调用了它的queryContentProviders方法。这是一个Binder对象,其实际调用的是PackageManagerService的queryContentProviders方法,如下所示:

public @NonNull ParceledListSlice<ProviderInfo> queryContentProviders(String processName,
        int uid, int flags, String metaDataKey) {
    final int callingUid = Binder.getCallingUid();
    final int userId = processName != null ? UserHandle.getUserId(uid)
            : UserHandle.getCallingUserId();
    if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
    flags = updateFlagsForComponent(flags, userId, processName);
    ArrayList<ProviderInfo> finalList = null;
    // reader
    synchronized (mPackages) {
        final Iterator<PackageParser.Provider> i = mProviders.mProviders.values().iterator();
        while (i.hasNext()) {
            final PackageParser.Provider p = i.next();
            PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
            if (ps != null && p.info.authority != null
                    && (processName == null
                            || (p.info.processName.equals(processName)
                                    && UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
                    && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {

                // See PM.queryContentProviders()'s javadoc for why we have the metaData
                // parameter.
                if (metaDataKey != null
                        && (p.metaData == null || !p.metaData.containsKey(metaDataKey))) {
                    continue;
                }
                final ComponentName component =
                        new ComponentName(p.info.packageName, p.info.name);
                if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {
                    continue;
                }
                if (finalList == null) {
                    finalList = new ArrayList<ProviderInfo>(3);
                }
                ProviderInfo info = PackageParser.generateProviderInfo(p, flags,
                        ps.readUserState(userId), userId);
                if (info != null) {
                    finalList.add(info);
                }
            }
        }
    }

    if (finalList != null) {
        Collections.sort(finalList, mProviderInitOrderSorter);
        return new ParceledListSlice<ProviderInfo>(finalList);
    }

    return ParceledListSlice.emptyList();
}

queryContentProviders方法很长,但是其主要做的事情是,遍历了mProviders.mProviders中的值,而mProviders.mProviders的一个ArrayMap,value为PackageParse.Provider对象。所以,我们需要知道mProviders.mProviders是在哪里被赋值的,最后找到了PackageManagerService.ProviderIntentResolver的addProvider方法,如下所示:

public final void addProvider(PackageParser.Provider p) {
    if (mProviders.containsKey(p.getComponentName())) {
        Slog.w(TAG, "Provider " + p.getComponentName() + " already defined; ignoring");
        return;
    }

    mProviders.put(p.getComponentName(), p);
    if (DEBUG_SHOW_INFO) {
        Log.v(TAG, "  "
                + (p.info.nonLocalizedLabel != null
                        ? p.info.nonLocalizedLabel : p.info.name) + ":");
        Log.v(TAG, "    Class=" + p.info.name);
    }
    final int NI = p.intents.size();
    int j;
    for (j = 0; j < NI; j++) {
        PackageParser.ProviderIntentInfo intent = p.intents.get(j);
        if (DEBUG_SHOW_INFO) {
            Log.v(TAG, "    IntentFilter:");
            intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
        }
        if (!intent.debugCheck()) {
            Log.w(TAG, "==> For Provider " + p.info.name);
        }
        addFilter(intent);
    }

该方法中,将参数中的PackageParser.Provider对象put了进来,而PackageParser.Provider对象存储的就是每一个ContentProvider的信息。在PackageManagerService的commitPackageSettings方法中调用了上述方法,commitPackageSettings方法如下所示:

private void commitPackageSettings(PackageParser.Package pkg, PackageSetting pkgSetting,
        UserHandle user, int scanFlags, boolean chatty) throws PackageManagerException {
	...省略
	//存储ContentProvider的信息
    int N = pkg.providers.size();
    StringBuilder r = null;
    int i;
    for (i=0; i<N; i++) {
        PackageParser.Provider p = pkg.providers.get(i);
        p.info.processName = fixProcessName(pkg.applicationInfo.processName,
                p.info.processName);
        mProviders.addProvider(p);
        p.syncable = p.info.isSyncable;
        if (p.info.authority != null) {
            String names[] = p.info.authority.split(";");
            p.info.authority = null;
            for (int j = 0; j < names.length; j++) {
                if (j == 1 && p.syncable) {
                    // We only want the first authority for a provider to possibly be
                    // syncable, so if we already added this provider using a different
                    // authority clear the syncable flag. We copy the provider before
                    // changing it because the mProviders object contains a reference
                    // to a provider that we don't want to change.
                    // Only do this for the second authority since the resulting provider
                    // object can be the same for all future authorities for this provider.
                    p = new PackageParser.Provider(p);
                    p.syncable = false;
                }
                if (!mProvidersByAuthority.containsKey(names[j])) {
                    mProvidersByAuthority.put(names[j], p);
                    if (p.info.authority == null) {
                        p.info.authority = names[j];
                    } else {
                        p.info.authority = p.info.authority + ";" + names[j];
                    }
                    if (DEBUG_PACKAGE_SCANNING) {
                        if (chatty)
                            Log.d(TAG, "Registered content provider: " + names[j]
                                    + ", className = " + p.info.name + ", isSyncable = "
                                    + p.info.isSyncable);
                    }
                } else {
                    PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
                    Slog.w(TAG, "Skipping provider name " + names[j] +
                            " (in package " + pkg.applicationInfo.packageName +
                            "): name already used by "
                            + ((other != null && other.getComponentName() != null)
                                    ? other.getComponentName().getPackageName() : "?"));
                }
            }
        }
        if (chatty) {
            if (r == null) {
                r = new StringBuilder(256);
            } else {
                r.append(' ');
            }
            r.append(p.info.name);
        }
    }
    if (r != null) {
        if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Providers: " + r);
    }

    //存储Service的信息
    N = pkg.services.size();
    r = null;
    for (i=0; i<N; i++) {
        PackageParser.Service s = pkg.services.get(i);
        s.info.processName = fixProcessName(pkg.applicationInfo.processName,
                s.info.processName);
        mServices.addService(s);
        if (chatty) {
            if (r == null) {
                r = new StringBuilder(256);
            } else {
                r.append(' ');
            }
            r.append(s.info.name);
        }
    }
    if (r != null) {
        if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Services: " + r);
    }

    //存储BroadcastReceiver的信息
    N = pkg.receivers.size();
    r = null;
    for (i=0; i<N; i++) {
        PackageParser.Activity a = pkg.receivers.get(i);
        a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                a.info.processName);
        mReceivers.addActivity(a, "receiver");
        if (chatty) {
            if (r == null) {
                r = new StringBuilder(256);
            } else {
                r.append(' ');
            }
            r.append(a.info.name);
        }
    }
    if (r != null) {
        if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Receivers: " + r);
    }

    //存储Activity的信息
    N = pkg.activities.size();
    r = null;
    for (i=0; i<N; i++) {
        PackageParser.Activity a = pkg.activities.get(i);
        a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                a.info.processName);
        mActivities.addActivity(a, "activity");
        if (chatty) {
            if (r == null) {
                r = new StringBuilder(256);
            } else {
                r.append(' ');
            }
            r.append(a.info.name);
        }
    }
    if (r != null) {
        if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Activities: " + r);
    }

    ...省略
}

可以看到,commitPackageSettings方法中不仅仅存储了ContentProvider的信息,还存储了Service、BroadcastReceiver、Activity等信息,而PackageParser.Provider对象是遍历PackageParser.Package的providers获取的,而PackageParser.Package的providers就是通过解析AndroidManifest.xml得到的。PackageParser的parseBaseApplication方法就是解析方法,如下所示:

private boolean parseBaseApplication(Package owner, Resources res,
            XmlResourceParser parser, int flags, String[] outError)
        throws XmlPullParserException, IOException {
    ...省略

    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            if (tagName.equals("activity")) {
                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
                        owner.baseHardwareAccelerated);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.activities.add(a);

            } else if (tagName.equals("receiver")) {
                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
                        true, false);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.receivers.add(a);

            } else if (tagName.equals("service")) {
                Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
                if (s == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.services.add(s);

            } else if (tagName.equals("provider")) {
                Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
                if (p == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.providers.add(p);

            } else {
                if (!RIGID_PARSER) {
                    Slog.w(TAG, "Unknown element under <application>: " + tagName
                            + " at " + mArchiveSourcePath + " "
                            + parser.getPositionDescription());
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                } else {
                    outError[0] = "Bad element under <application>: " + tagName;
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
            }
    }
    ...省略

    return true;
}

可以看到对于AndroidManifest中的tagName为provider,则解析其中的信息并生成PackageParser.Provider对象。

通过PMS获取ContentProvider列表信息的流程到此结束,通过上述分析,我们也验证了自定义的ContentProvider必须在AndroidManifest中注册这样的一个结论。

我们继续回到AMS的attachApplicationLocked方法中,在拿到了应用注册的ContentProvider信息后,远程调用了ApplicationThread的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 enableBinderTracking, boolean trackAllocation,
        boolean isRestrictedBackupMode, boolean persistent, Configuration config,
        CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
        String buildSerial) {

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

    setCoreSettings(coreSettings);

    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.enableBinderTracking = enableBinderTracking;
    data.trackAllocation = trackAllocation;
    data.restrictedBackupMode = isRestrictedBackupMode;
    data.persistent = persistent;
    data.config = config;
    data.compatInfo = compatInfo;
    data.initProfilerInfo = profilerInfo;
    data.buildSerial = buildSerial;
    sendMessage(H.BIND_APPLICATION, data);
}

注意:参数中的上述的providers就是从PMS中获取的ContentProvider信息。

该方法发送了一个消息,消息ID是H.BIND_APPLICATION。在mH对象的handleMessage方法中处理该消息,并调用handleBindApplication方法,如下所示:

private void handleBindApplication(AppBindData data) {
    ...省略

    Application app;
    final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
    final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
    try {
        app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;

        if (!data.restrictedBackupMode) {
             //ContentProvider列表不为空
            if (!ArrayUtils.isEmpty(data.providers)) {
                installContentProviders(app, data.providers); //初始化并加载ContentProvider
                // 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);
            }
        }

        try {
            mInstrumentation.onCreate(data.instrumentationArgs);
        }
        catch (Exception e) {
            throw new RuntimeException(
                "Exception thrown in onCreate() of "
                + data.instrumentationName + ": " + e.toString(), e);
        }
        //调用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);
            }
        }
    } finally {
        // If the app targets < O-MR1, or doesn't change the thread policy
        // during startup, clobber the policy to maintain behavior of b/36951662
        if (data.appInfo.targetSdkVersion <= Build.VERSION_CODES.O
                || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
            StrictMode.setThreadPolicy(savedPolicy);
        }
    }

    ...省略
}

在handleBindApplication方法中,判断如果ContentProvider列表不为空,则调用installContentProviders方法,如下所示:

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

    for (ProviderInfo cpi : providers) {
        ContentProviderHolder cph = installProvider(context, null, cpi,
                false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
        if (cph != null) {
            cph.noReleaseNeeded = true;
            results.add(cph);
        }
    }

    try {
        ActivityManager.getService().publishContentProviders(
            getApplicationThread(), results);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

对于传递过来的ContentProvider列表,遍历该列表,取出每一个ProviderInfo对象,并调用installProvider方法(第二个参数传过来的是null)。如下所示:

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) {
        if (DEBUG_PROVIDER || noisy) {
            Slog.d(TAG, "Loading provider " + info.authority + ": "
                    + info.name);
        }
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        if (context.getPackageName().equals(ai.packageName)) {
            c = context;
        } else if (mInitialApplication != null &&
                mInitialApplication.getPackageName().equals(ai.packageName)) {
            c = mInitialApplication;
        } else {
            try {
                c = context.createPackageContext(ai.packageName,
                        Context.CONTEXT_INCLUDE_CODE);
            } catch (PackageManager.NameNotFoundException e) {
                // Ignore
            }
        }
        if (c == null) {
            Slog.w(TAG, "Unable to get context for package " +
                  ai.packageName +
                  " while loading content provider " +
                  info.name);
            return null;
        }

        if (info.splitName != null) {
            try {
                c = c.createContextForSplit(info.splitName);
            } catch (NameNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

        try { 
        	//通过类生成器生成了ContentProvider对象
            final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
            ...省略
            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;
        }
    } else {
        ...省略
    }

    ...省略
    return retHolder;
}

由于上一个方法中传递进来的holder为null,则走holder == null的逻辑,其中通过类生成器生成了ContentProvider对象,并且调用了ContentProviderProvider对象的attachInfo方法,attachInfo方法调用了另一个attachInfo重载方法,如下所示:

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
    mNoPerms = testing;

    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对象的onCreate方法。这也证明了一个结论:ContentProvider的onCreate方法是在UI线程执行的。

小结:通过上述的源码分析,我们知道了ContentProvider是在AndroidManifest中注册,并且在App启动时通过反射的形式创建的,创建后并且调用了每一个ContentProvider对象的onCreate方法。然后调用了Application对象的onCreate方法,整个App就这样运行起来了。

2、ContentProvider的调用

上面分析了ContentProvider对象是如何创建和初始化的,承接上面的内容,我们继续分析外部应用是如何调用ContentProvider的CRUD方法来和ContentProvider交互的。

我们拿其中的一个方法query进行分析,其他方法的流程与之类似,就不再重复分析了。

我们知道,在客户端应用中,调用ContentProvider的query方法是通过Context的getContentResolver().query方法来实现的,而Context的实现者对象是ContextImpl,其getContentResolver方法如下所示:

public ContentResolver getContentResolver() {
      return mContentResolver;
 }

很简单的返回了一个ContentResolver对象,而上面的mContenResolver实际类型是ApplicationContentResolver,所以getContentResolver().query方法实际上执行的是ApplicationContentResolver.query方法,而ApplicationContentResolver并没有重写query方法,所以query方法还是在ContentResolver中实现,如下所示:

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);
        } 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);
            stableProvider = acquireProvider(uri);
            if (stableProvider == null) {
                return null;
            }
            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;
    } 
    ...省略
}

query方法中,获取数据的步骤主要有以下两步:

  • 通过acquireUnstableProvider方法获取IContentProvider,这是Binder对象。
  • 通过该Binder对象远程调用ContentProvider的query方法得到Cursor对象。

我们先来看第一步,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;
}

一上来就判断了Uri的合法性,如果Uri的Scheme没有以SCHEME_CONTENT开头,则返回null。**SCHEME_CONTENT的值是"content",说明了和ContentProvider交互的Uri必须以"content"作为scheme。**然后调用了acquireUnstableProvider方法,acquireUnstableProvider在子类中实现,即在ApplicationContentResolver中实现,如下所示:

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

acquireUnstableProvider方法又调用了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 {
    	//缓存中没有,远程调用AMS的getContentProvider方法返回IContentProvider对象
        holder = ActivityManager.getService().getContentProvider(
                getApplicationThread(), auth, userId, stable);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    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;
}

如果缓存中有IContentProvider对象,则从缓存中获取;否则从远程调用AMS获取IContentProvider对象。

小结:这里说明了和ContentProvider交互是进程间的通信,而中间桥梁就是AMS。

通过AMS拿到了服务端应用的ContentProvider的代理对象IContentProvider,返回到上面的query方法,通过IContentProvider对象调用其query方法返回了Cursor对象给到客户端调用者。

我们知道,IContentProvider对象的实现者是ContentProvider.Transport。因此客户端通过IContentProvider远程调用query方法,实际上调用的是ContentProvider.Transport的query方法,如下所示:

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) {
        if (projection != null) {
            return new MatrixCursor(projection, 0);
        }

        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方法,最终的结果再通过Binder返回给客户端调用者。这样就完成了客户端远程调用ContentProvider的query方法的整个过程。

三、总结

客户端应用和服务端应用的ContentProvider交互是进程间的通信,其底层是通过Binder实现的。 ContentProvider对象在App启动时会创建并初始化,其onCreate方法是运行在服务端的主进程的;外部应用调用ContentProvider的CRUD方法是进程间的调用,是运行在系统的Binder进程的。

Android 开发相关源码精编解析

随着Android开发行业逐渐饱和,对Android开发者的面试要求也越来越高,是否掌握底层源码,便是考验一名Android开发者的重要一环。面试被问到源码问题答不出来,会掉身价、砍薪资尚且不谈,甚至连面试都过不了!

网上各类源码解析的文章博客五花八门、良莠不齐。杂乱、要么内容质量太浅,零散、碎片化,总看着看着就衔接不上了。

所以特意将我在疫情期间花了4个月整理出来的《Android 开发相关源码精编解析》分享出来大家

由于内容较多,避免影响到大家的阅读体验,在此只截图展示目录部分,487详细完整版的《Android 开发相关源码精编解析》电子书文档领取方式:**点赞+关注,然后私信关键词 【666】**即可加我的个人微信私发给你(无偿)。也欢迎大家找我探讨Android技术问题~

**目录:一共18节,487页PDF,**包括MMKV 源码,ARouter 源码,AsyncTask 源码,Volley 源码,Retrofit 源码,OkHttp 源码,ButterKnife 源码,Okio 源码,SharedPreferences 源码,EventBus 源码,Android 自定义注解初探,View 的工作机制源码分析,Android 触摸事件分发机制源码分析,Android 按键事件分发机制源码分析,深入解析 Handler 源码,深入解析 Binder 源码,深入解析 JNI 源码,深入解析 Glide 源码。

资料已经上传在我的GitHub,无偿分享的

Android高级面试精选题、架构师进阶实战文档传送门:我的GitHub

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断!!!

猜你喜欢

转载自blog.csdn.net/zzz777qqq/article/details/109856439