ContentProvider 启动流程

242 阅读5分钟

通过这篇文章,我们来梳理一下 ContentProvider 启动的整体流程以及其中我们应该注意的细节

ContentProvider 启动的整体流程

ContentProvider 运行所在进程与 APP 进程相同

当 ContentProvider 运行所在进程与 APP 进程相同时,ContentProvider 会伴随 APP 进程初始化一起启动

APP 进程在初始化时,会创建所有运行在本进程中的 ContentProvider 实例,并回调所有 ContentProvider 实例的 onCreate() 函数,完成启动

当所有 ContentProvider 启动完成后,会将这些 ContentProvider 的信息发送给 AMS 进行记录,这样其他进程就可以通过 AMS 来访问这些 ContentProvider 了

总体流程如下图:

ContentProvider 运行所在进程与 APP 进程不同

当 ContentProvider 运行所在进程与 APP 进程不同时,ContentProvider 不会在 APP 进程初始化时被启动

当 APP 进程通过 getContentResolver() 函数访问 ContentProvider 时,会访问 AMS 尝试获取目标 ContentProvider 的 Binder 通信接口

如果 AMS 中保存有该 ContentProvider 的 Binder 通信接口就直接返回给 APP 进程

如果 AMS 中没有保存有该 ContentProvider 的 Binder 通信接口,说明该 ContentProvider 还未启动,新建进程启动目标 ContentProvider,最终回调目标 ContentProvider 实例的 onCreate() 函数,完成启动

当目标 ContentProvider 启动完成后,再将目标 ContentProvider 的信息发送给 AMS 进行记录

总体流程如下图:

源码

ContentProvider 启动流程可以分为两个阶段:

  1. ContentProvider 进程初始化
  2. 访问 ContentProvider

ContentProvider 进程初始化

当 ContentProvider 进程创建完成后,就会进行初始化,调用到 ActivityThread 的 main() 函数

ActivityThread.main()

// frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args)
{
    ......

    thread.attach(false, startSeq);

    ......
}

private void attach(boolean system, long startSeq)
{
    ......

    // AIDL 调用 AMS 的 attachApplication 函数
    final IActivityManager mgr = ActivityManager.getService();
    mgr.attachApplication(mAppThread, startSeq);

    ......
}

ActivityManagerService.attachApplication()

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public final void attachApplication(IApplicationThread thread, long startSeq)
{
    ......

    attachApplicationLocked(thread, callingPid, callingUid, startSeq);

    ......
}

private final boolean attachApplicationLocked(
        IApplicationThread thread, // thread = App 进程的 ApplicationThread
        int pid, int callingUid, long startSeq
){
    ......

    // 此时进程已经启动,mProcessesReady = true
    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    // 获取 APP 应用的 AndroidManifest 文件中注册的 ContentProvider 信息
    List<ProviderInfo> providers = normalMode ? 
                    generateApplicationProvidersLocked(app) : null;

    ......

    final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
    // AIDL 调用 APP 进程 ApplicationThread 的 bindApplication 函数
    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);

    ......
}

ActivityManagerService.generateApplicationProvidersLocked()

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app)
{
    ......

    // AIDL 调用 PackageManagerService 的 queryContentProviders 函数,
    // 获取 APP 应用中所有经过 AndroidManifest 注册的 ContentProvider 信息
    providers = AppGlobals.getPackageManager()
                    .queryContentProviders(app.processName, app.uid,
                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
                                    | MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null)
                    .getList();

    ......
}

PackageManagerService.queryContentProviders()

当 APP 应用进行安装时,PackageManagerService 会通过 ComponentResolver 解析 APK 包中的 AndroidManifest 文件,获取到所有四大组件的信息并保存

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public @NonNull ParceledListSlice<ProviderInfo> queryContentProviders(
    		String processName, // 当前进程名
                int uid, // 当前线程 uid
    		int flags, String metaDataKey
){
    ......
    
    // 最终的结果集
    ArrayList<ProviderInfo> finalList = null;
    // 调用 ComponentResolver 的 queryProviders 函数,
    // 获取 APP 应用中所有经过 AndroidManifest 注册的 ContentProvider 信息
    final List<ProviderInfo> matchList = 
    mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId);
    final int listSize = (matchList == null ? 0 : matchList.size());
    synchronized (mLock)
    {
        // 遍历 matchList,进行筛选
        for (int i = 0; i < listSize; i++)
        {
            // 获取 ContentProvider 信息
            final ProviderInfo providerInfo = matchList.get(i);
			
            // 这部分是筛选条件代码,其中大部分与流程无关,有兴趣可以自行了解
            // 如果该 ContentProvider 信息不满足条件,会调用 continue 语句跳过当前循环,
            // 不会添加到最终的结果集
            ......
            
            // 将该 ContentProvider 信息添加到最终的结果集
            finalList.add(providerInfo);
        }
    }

    if (finalList != null)
    {
        finalList.sort(sProviderInitOrderSorter);
        // 返回最终的结果集
        return new ParceledListSlice<>(finalList);
    }

    return ParceledListSlice.emptyList();
}

ComponentResolver.queryProviders()

// frameworks/base/services/core/java/com/android/server/pm/ComponentResolver.java
List<ProviderInfo> queryProviders(String processName, String metaDataKey, 
                                  int uid, int flags, int userId
){
    ......

    // 最终的结果集
    List<ProviderInfo> providerList = null;

    ......

    synchronized (mLock)
    {
        // 经过解析得到的所有可用的 ContentProvider 信息都保存在 mProviders.mProviders 中,
        // 遍历 mProviders.mProviders
        for (int i = mProviders.mProviders.size() - 1; i >= 0; --i)
        {
            //  ContentProvider 信息
            final ParsedProvider p = mProviders.mProviders.valueAt(i);
            
            // 这部分是筛选条件代码,其中大部分与流程无关,有兴趣可以自行了解
            // 如果当前 ContentProvider 信息不满足条件,会调用 continue 语句跳过当前循环,
            // 不会添加到最终的结果集
            ......

            // 判断该 ContentProvider 是否指定了运行所在的进程
            // (如果没指定 ContentProvider 默认运行在 APP 进程中)
            // 如果指定的进程名与 APP 进程名不同,则不把该 ContentProvider 加入最终的结果集
            if (processName != null && (!p.getProcessName().equals(processName)
                    || !UserHandle.isSameApp(pkg.getUid(), uid)))
            {
                continue;
            }
            
            ......
                
            final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
                        pkg, p, flags, state, appInfo, userId, ps);
            if (info == null)
            {
                continue;
            }
            if (providerList == null)
            {
                providerList = new ArrayList<>(i + 1);
            }
            // 将该 ContentProvider 信息添加到最终的结果集
            providerList.add(info);
        }
    }
    // 返回最终的结果集
    return providerList;
}

ApplicationThread.bindApplication()

// frameworks/base/core/java/android/app/ActivityThread$ApplicationThread.java
public final void bindApplication(String processName, ApplicationInfo appInfo, 
                                  ......
){
    ......

    // 发送消息 (H.BIND_APPLICATION) 给 ActivityThread 中的内部类 H (Handler)
    sendMessage(H.BIND_APPLICATION, data);
}

H.handleMessage()

// frameworks/base/core/java/android/app/ActivityThread$H.java
public void handleMessage(Message msg)
{
    switch (msg.what)
    {
        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()

// frameworks/base/core/java/android/app/ActivityThread.java
private void handleBindApplication(AppBindData data)
{
    ......

    // 创建 ContextImpl
    final ContextImpl instrContext = ContextImpl.createAppContext(this, pi, 
                appContext.getOpPackageName());

    // 创建 mInstrumentation
    final ClassLoader cl = instrContext.getClassLoader();
    mInstrumentation = (Instrumentation)
                cl.loadClass(data.instrumentationName.getClassName()).newInstance();
    
    ......

    // 创建 Application
    app = data.info.makeApplication(data.restrictedBackupMode, null);
    
    ......

    // 启动当前进程的 ContentProvider
    installContentProviders(app, data.providers);
    
    ......

    // 回调 Application 的 onCreate 函数
    mInstrumentation.callApplicationOnCreate(app);

    ......
}

ActivityThread.installContentProviders()

// frameworks/base/core/java/android/app/ActivityThread.java
private void installContentProviders(Context context, List<ProviderInfo> providers)
{
    final ArrayList<ContentProviderHolder> results = new ArrayList<>();

    // 遍历当前进程的 ProviderInfo (存储 ContentProvider 的信息) 列表
    for (ProviderInfo cpi : providers)
    {
        ......

        // 调用 installProvider 函数,启动 ContentProvider
        ContentProviderHolder cph = installProvider(context, null, cpi, 
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
        if (cph != null)
        {
            cph.noReleaseNeeded = true;
            results.add(cph);
        }
    }

    try
    {
        // AIDL 调用 AMS 的 publishContentProviders 函数,将这些 ContentProvider 进行发布,
        // 这样其他进程就可以通过 AMS 来访问这些 ContentProvider 了
        ActivityManager.getService().publishContentProviders(getApplicationThread(), results);
    }
    catch (RemoteException ex)
    {
        throw ex.rethrowFromSystemServer();
    }
}

ActivityThread.installProvider()

// frameworks/base/core/java/android/app/ActivityThread.java
private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, // 此时 holder = null
            ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable
){
    ContentProvider localProvider = null;
    IContentProvider provider;

    // if 命中
    if (holder == null || holder.provider == null)
    {
        ......

        // 通过反射创建 ContentProvider
        final java.lang.ClassLoader cl = c.getClassLoader();
        LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
        localProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);
        provider = localProvider.getIContentProvider();

        ......

        // 为此 ContentProvider 创建上下文
        localProvider.attachInfo(c, info);

        ......
    }
    else
    {
        ......
    }

    ......
}

ContentProvider.attachInfo()

// frameworks/base/core/java/android/content/ContentProvider.java
public void attachInfo(Context context, ProviderInfo info)
{
    attachInfo(context, info, false);
}

private void attachInfo(Context context, ProviderInfo info, boolean testing)
{
    ......

    if (mContext == null)
    {
        mContext = context;

        ......

        mMyUid = Process.myUid();
        // 记录 ContentProvider 的设置 (例如访问权限、authority 等)
        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

ContextImpl.getContentResolver()

// frameworks/base/core/java/android/app/ContextImpl.java
public ContentResolver getContentResolver()
{
    return mContentResolver;
}

private final ApplicationContentResolver mContentResolver;

private ContextImpl(ContextImpl container, ActivityThread mainThread, 
                    ......
){
    ......
    
    mContentResolver = new ApplicationContentResolver(this, mainThread);
}

getContentResolver() 函数会返回一个 ApplicationContentResolver 对象,该对象在 ContextImpl 被创建时一同被创建

ApplicationContentResolver 继承自 ContentResolver,但是却没有重写 ContentResolver 对应的 CRUD 函数

以 insert 调用流程为例,getContentResolver().insert() 最终会调用到 ContentResolver.insert() 函数

ContentResolver.insert()

// frameworks/base/cre/java/android/content/ContentResolver.java
public final @Nullable Uri insert(Uri url, ContentValues values)
{
    return insert(url, values, null);
}

public final @Nullable Uri insert(Uri url, ContentValues values, Bundle extras)
{
    ......

    // 获取 ContentProvider 的 Binder 通信接口
    IContentProvider provider = acquireProvider(url);

    ......

    // AIDL 调用 ContentProvider 的 insert 函数
    Uri createdRow = provider.insert(mPackageName, mAttributionTag, url, values, extras);

    ......

    return createdRow;

    ......
}

public final IContentProvider acquireProvider(Uri uri)
{
    ......

    return acquireProvider(mContext, auth);

    ......
}

protected abstract IContentProvider acquireProvider(Context c, String name);

ApplicationContentResolver 实现了 acquireProvider() 函数

ApplicationContentResolver.acquireProvider()

// frameworks/base/core/java/android/app/ContextImpl$ApplicationContentResolver.java
protected IContentProvider acquireProvider(Context context, String auth)
{
    // 调用 ActivityThread 的 acquireProvider 函数
    return mMainThread.acquireProvider(context,
                ContentProvider.getAuthorityWithoutUserId(auth),
                resolveUserIdFromAuthority(auth), true);
}

ActivityThread.acquireProvider()

// frameworks/base/core/java/android/app/ActivityThread.java
public final IContentProvider acquireProvider(Context c, String auth, 
                                              int userId, boolean stable
){
    // 查看该 ContentProvider 的 Binder 通信接口是否有缓存,
    // 如果有缓存说明该 ContentProvider 不是第一次访问,直接返回缓存中的 Binder 通信接口
    final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
    if (provider != null)
    {
        return provider;
    }
    
    ContentProviderHolder holder = null;
    
    ......

    // 如果没有缓存,AIDL 调用 AMS 的 getContentProvider 函数,
    // 获取该 ContentProvider 的信息
    holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
    
    ......
        
    holder = installProvider(c, holder, holder.info,
                    true /*noisy*/, holder.noReleaseNeeded, stable);
    return holder.provider;
}

ActivityManagerService.getContentProvider()

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public final ContentProviderHolder getContentProvider(
                IApplicationThread caller, String callingPackage, String name, 
    		int userId, boolean stable
){
    ......

    return getContentProviderImpl(caller, name, null, callingUid, callingPackage, 
                    null, stable, userId);
}

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller, 
			String name, IBinder token, int callingUid, String callingPackage, 
			String callingTag, boolean stable, int userId
){
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;
    ProviderInfo cpi = null;
    boolean providerRunning = false;
    
    synchronized(this)
    {
        ......

        // 首先检查该 ContentProvider 是否已经在 AMS 上发布
        // (发布过的 ContentProvider 信息会缓存在 mProviderMap 中)
        cpr = mProviderMap.getProviderByName(name, userId);

        ......

        ProcessRecord dyingProc = null;
        // 如果该 ContentProvider 已经发布 且 运行所在进程已经被创建
        if (cpr != null && cpr.proc != null)
        {
            // 该 ContentProvider 运行所在进程没有被杀就说明该 ContentProvider 仍在运行中
            providerRunning = !cpr.proc.killed;

            ......
        }

        // 如果该 ContentProvider 仍在运行中
        if (providerRunning)
        {
            ......

            // 检查是否允许该 ContentProvider 在多个进程中运行,
            // 如果允许,直接返回一个空的 ContentProviderHolder
            if (r != null && cpr.canRunHere(r))
            {
                ......

                ContentProviderHolder holder = cpr.newHolder(null);
                holder.provider = null;
                return holder;
            }
            
            ......
        }

        // 如果该 ContentProvider 没有在运行,说明该 ContentProvider 还未启动
        if (!providerRunning)
        {
            ......

            // AIDL 调用 PackageManagerService 的 resolveContentProvider 函数,
            // 通过 authorities 标识查找该 ContentProvider 的信息
            cpi = AppGlobals.getPackageManager().
                    resolveContentProvider(name,
                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);

            ......

            // 获取该 ContentProvider 的全类名
            ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
            // 再次检查该 ContentProvider 是否已经在 AMS 上发布
            cpr = mProviderMap.getProviderByClass(comp, userId);
            // 如果没有发布,说明该 ContentProvider 是第一次启动
            boolean firstClass = cpr == null;
            if (firstClass)
            {
                ......

                // AIDL 调用 PackageManagerService 的 getApplicationInfo 函数,
                // 获取 APP 应用的相关设置信息
                ApplicationInfo ai = AppGlobals.getPackageManager().
                        getApplicationInfo(cpi.applicationInfo.packageName, STOCK_PM_FLAGS, userId);

                ......

                // 通过 APP 应用的相关设置信息构建 ContentProviderRecord
                cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
            }

            ......

            // 检查是否允许该 ContentProvider 在多个进程中运行,
            // 如果允许,直接返回一个空的 ContentProviderHolder
            if (r != null && cpr.canRunHere(r))
            {
                return cpr.newHolder(null);
            }

            ......

            // 所有正在被启动的 ContentProvider 都缓存在 mLaunchingProviders 中,
            // 在启动该 ContentProvider 前,
            // 检查该 ContentProvider 是否正在被其它的 APP 应用启动
            final int N = mLaunchingProviders.size();
            int i;
            for (i = 0; i < N; i++)
            {
                if (mLaunchingProviders.get(i) == cpr)
                {
                    break;
                }
            }

            // 如果该 ContentProvider 没有正在被其它的 APP 应用启动
            if (i >= N)
            {
                ......

                // 获取该 ContentProvider 运行所在进程的信息,判断该进程是否已经启动
                ProcessRecord proc = getProcessRecordLocked(
                            cpi.processName, cpr.appInfo.uid, false);
                // 如果进程信息不为空,则代表该进程已启动
                if (proc != null && proc.thread != null && !proc.killed)
                {
                    ......

                    // AIDL 调用该进程中 ApplicationThread 的 scheduleInstallProvider 函数,
                    // 最终调用 ActivityThread 的 installContentProviders 函数
                    proc.thread.scheduleInstallProvider(cpi);

                    ......
                }
                else // 如果该 ContentProvider 运行所在进程没有启动
                {
                    ......

                    // 访问 Zygote 进程请求启动一个新进程用于运行该 ContentProvider
                    proc = startProcessLocked(cpi.processName,
                            cpr.appInfo, false, 0,
                            new HostingRecord("content provider",
                            new ComponentName(cpi.applicationInfo.packageName,cpi.name)),
                            ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);

                    ......
                }
                cpr.launchingApp = proc;
                // 将该 ContentProvider 的信息放入 mLaunchingProviders 缓存
                mLaunchingProviders.add(cpr);
            }

            // 如果该 ContentProvider 是第一次启动
            if (firstClass)
            {
                // 将该 ContentProvider 的信息放入 mProviderMap 缓存
                mProviderMap.putProviderByClass(comp, cpr);
            }

            mProviderMap.putProviderByName(name, cpr);

            ......
        }
    }

    // 如果该 ContentProvider 需要新起进程来运行,AMS 线程会进入睡眠等待,
    // 直至 ContentProvider 发布完成,其中的代码细节与流程无关,有兴趣可以自行了解
    ......

    return cpr.newHolder(conn);
}

这个函数的代码非常长,因此逻辑略显复杂,具体逻辑如下图所示

ActivityManagerService.getContentProvider() 函数执行完成返回后,会返回执行 ActivityThread.installProvider() 函数

根据返回的 ContentProviderHolder 是否为空来判断是否要在 APP 进程创建该 ContentProvider 的实例 (multiprocess 属性)

ActivityThread.installProvider()

private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable
){
    ContentProvider localProvider = null;
    IContentProvider provider;

    // 如果该 ContentProvider 设置了 multiprocess 属性,执行 else
    if (holder == null || holder.provider == null)
    {
        ......
    }
    else
    {
        provider = holder.provider;
    }

    ......
}

之后 APP 进程就可以通过该 ContentProvider 的 Binder 通信接口 (ContentProviderProxy),AIDL 调用该 ContentProvider 的 CRUD 函数

总结

本篇文章分析了 ContentProvider 的启动流程,将其中对于我们开发者而言比较有意义的部分代码进行了分析

由于 ContentProvider 可以单独运行在自己的进程,需要多情况讨论,因此整体流程相对而言会有一些复杂

了解 ContentProvider 启动流程,对于我们理解 ContentProvider 的运行和进行启动优化有着很大的帮助

但源码还是非常庞大的,没办法面面俱到,希望读者还是能参照本篇文章尝试自己去阅读源码,加深理解