美文网首页Android开发经验谈Android技术知识Android开发
腾讯微视Android高级岗:说说ContentProvider

腾讯微视Android高级岗:说说ContentProvider

作者: 字节走动_Android | 来源:发表于2020-10-12 22:17 被阅读0次

    一、引言

    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进程的。

    文末

    对文章有何见解,或者有何技术问题,都可以在评论区一起留言讨论,一定会回复的。
    也欢迎大家来我的B站找我玩,各类Android架构师进阶技术难点的视频讲解,任君白嫖~
    B站直通车:https://space.bilibili.com/484587989

    喜欢文章的小伙伴别忘了点个关注,留个赞再走呀,一个专注Android面试的小喵喵~

    相关文章

      网友评论

        本文标题:腾讯微视Android高级岗:说说ContentProvider

        本文链接:https://www.haomeiwen.com/subject/ctappktx.html