基于版本:Android R

0. 前言

很早以前分析过service(Android 中service 详解) 和broadcast receiver(android 中的 Broadcast 机制详解) 的机制,最近在回顾的时候发现少了ContentProvider 和 Activity 的流程,抽些时间补充。这一篇主要分析ContentProvider 的原理。

《Android基础总结之八:ContentProvider》中举例说明ContentProvider 的调用流程,涉及到的接口比较多,我们这里以query 为入口进行剖析。

1. 入口--ContentResolver

调用接口类似:

ContentResolver cr = mContext.getContentResolver();
cr.query(...);

使用ContentResolver的原因:

  • ContentResolver 是不同的应用与不同或相同的ContentProvider 交互的枢纽;
  • ContentResolver 不但管理ContentProvider 状态的notify,也管理应用端ContentObserver的转接;
  • 让架构更清晰,应用端只需要与ContentResolver 交互,ContentProvider只需要负责数据维护、数据状态更新;
  • ContentProvider 为单独的进程,作为通信的server 端,ContentResolver 是应用为了交互使用,作为通信的Client 端;

1.1 getContentResolver

frameworks/base/core/java/android/app/ContextImpl.java

@Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }
private ContextImpl(...) {
    ...
    mContentResolver = new ApplicationContentResolver(this, mainThread);
}
private static final class ApplicationContentResolver extends ContentResolver {
        @UnsupportedAppUsage
        private final ActivityThread mMainThread;

        public ApplicationContentResolver(Context context, ActivityThread mainThread) {
            super(context);
            mMainThread = Objects.requireNonNull(mainThread);
        }

        ...
    }

ContentResolver的源头是ApplicationContentResolver,继承自ContentResolver。

ApplicationContentResolver中记录这App 的context 和mainThread。

1.2 ContentResolver 中重要的接口

frameworks/base/core/java/android/content/ContentResolver.java

public abstract class ContentResolver implements ContentInterface {
    ...

    public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable String selection,
            @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        ...
    }

    public final @Nullable String getType(@NonNull Uri url) {
        ...
    }

    public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
                @Nullable ContentValues values) {
        ...
    }

    public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where,
            @Nullable String[] selectionArgs) {
        ...
    }

    public final int update(@RequiresPermission.Write @NonNull Uri uri,
            @Nullable ContentValues values, @Nullable String where,
            @Nullable String[] selectionArgs) {
        ...
    }

    public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
            ContentObserver observer, @UserIdInt int userHandle) {
        ...
    }

    public final void unregisterContentObserver(@NonNull ContentObserver observer) {
        ...
    }
}

 这里没有细列举,应为ContentResolver和ContentProvider 都是继承自interface ContentInterface,所以,query/insert/delete/update/getType 等接口都是需要实现的。在此基础上ContentResolver还负责ContentObserver的register 和unRegister 工作。

2. query

public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable CancellationSignal cancellationSignal) {
        Objects.requireNonNull(uri, "uri");

        ...

        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        if (unstableProvider == null) {
            return null;
        }
        IContentProvider stableProvider = null;
        Cursor qCursor = null;
        try {
            long startTime = SystemClock.uptimeMillis();

            ...

            try {
                qCursor = unstableProvider.query(mPackageName, mAttributionTag, 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, mAttributionTag, uri, projection,
                        queryArgs, remoteCancellationSignal);
            }
            if (qCursor == null) {
                return null;
            }

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

            // Wrap the cursor object into CursorWrapperInner object.
            final IContentProvider provider = (stableProvider != null) ? stableProvider
                    : acquireProvider(uri);
            final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
            stableProvider = null;
            qCursor = null;
            return wrapper;
        } catch (RemoteException e) {
            ...
        } finally {
            ...
        }
    }

 代码比较长,大概可以分以下几个部分:

  • acquireUnstableProvider 获取ContentProvider 的proxy;
  • 通过ContentProvider 的proxy 调用query 接口进行查询;
  • 如果查询中unstable provider 出现dead,应用会尝试再拉一次;
  • 实例CursorWrapperInner 并返回;

2.1 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;
    }

acquireUnstableProvider 是个抽象函数,实现在ApplicationContentResolver中:

frameworks/base/core/java/android/app/ContextImpl.java#ApplicationContentResolver

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

 最终调用的是ActivityThread.acquireProvider。

2.1.1 acquireProvider

frameworks/base/core/java/android/app/ActivityThread.java

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;
        }

        try {
            synchronized (getGetProviderLock(auth, userId)) {
                holder = ActivityManager.getService().getContentProvider(
                        getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
            }
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        if (holder == null) {
            ...
            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;
    }

 大概分三部分:

  • acquireExistingProvider 函数用于确认是否ActivithThread 中已经存有provider,如果存有则直接返回,防止反复去AMS 中acquire;
  • 通过AMS.getContentProvider 函数获取符合auth 和userId的 provider proxy;
  • 通过installProvider 让ActivityThread 持有provider;

这里的AMS.getContentProvider 以单独的一节讲解,详细请看第 3 节。

这里的installProvider 关注3.4.4节;

另外,最终获取到的provider proxy 是通过getIContentProvider 接口而来,或者的流程详细见3.4.4节,getIContentProvider 接口讲解看第 4 节。

3. AMS.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);
    }

getContentProviderImpl 代码比较长,这里分开剖析。

3.1 getRecordForAppLocked

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, int callingUid, String callingPackage, String callingTag,
            boolean stable, int userId) {
        ...

        synchronized(this) {
            ...
            ProcessRecord r = null;
            if (caller != null) {
                r = getRecordForAppLocked(caller);
                if (r == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                          + " (pid=" + Binder.getCallingPid()
                          + ") when getting content provider " + name);
                }
            }

根据传入的caller,类型为IApplicationThread,获得调用者App 的ProcessRecord,要求App 必须是存在的。

3.2 确认provider 是否已经存在

// First check if this content provider has been published...
            cpr = mProviderMap.getProviderByName(name, userId);

下面的代码主要是根据provider 是否已经发布进行分段,主要分下面几个部分:

  • 如果provider 已经running;
  • 如果 provider 尚未running;
  • 如果provider 已经启动,当未发布;
  • 如果provider 没有启动;
  • 等待provider 发布;

3.3 如果provider已经running

if (providerRunning) {
                cpi = cpr.info;
                String msg;

                //当允许运行在调用者进程且已发布,则直接返回
                if (r != null && cpr.canRunHere(r)) {
                    // 检查权限
                    ...

                    ContentProviderHolder holder = cpr.newHolder(null);
                    holder.provider = null; //注意这里的provider 为null,详细见3.4.4
                    return holder;
                }

                //如果provider不是在caller进程中,需要继续检查caller 的权限
                ...

                //权限检查ok,则返回已经存在的provider 的connection,并增加引用计数
                conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
                        stable);
                if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
                    if (cpr.proc != null
                            && r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
                        //更新进程LRU队列updateLruProcess");
                        mProcessList.updateLruProcessLocked(cpr.proc, false, null);
                    }
                }


                //更新进程adj
                final int verifiedAdj = cpr.proc.verifiedAdj;
                boolean success = updateOomAdjLocked(cpr.proc, true,
                        OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);

                if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
                    success = false;
                }
                ...
                if (!success) {//如果失败了,provider进程已经被kill,减少引用计数
                    boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
                    if (!lastRef) {
                        // This wasn't the last ref our process had on
                        // the provider...  we will be killed during cleaning up, bail.
                        return null;
                    }
                    // We'll just start a new process to host the content provider
                    providerRunning = false;
                    conn = null;
                    dyingProc = cpr.proc;
                } else {
                    cpr.proc.verifiedAdj = cpr.proc.setAdj;
                }

                ...
            }

如果ContentProvider 所在进程已经存在时:

  • 权限检查;
  • 当允许运行在调用者进程,直接返回;
  • 增加引用计数;
  • 更新进程LRU队列;
  • 更新进程adj;
  • 当provider被kill时,减少引用计数并调用appDiedLocked,且设置ContentProvider为未running状态,并记录dyingProc;

注意:当允许运行在发布者的进程中时,holder.provider 为null,installProvider 函数中需要重新创建provider,详细见3.4.4节。

3.4 如果provider没有running

if (!providerRunning) {
                try {
                    //根据authority,获取ProviderInfo对象
                    cpi = AppGlobals.getPackageManager().
                        resolveContentProvider(name,
                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
                } catch (RemoteException ex) {
                }
                if (cpi == null) {//不存在这个provider,则返回null
                    return null;
                }

                //provider是否为system,或者是PHONE_UID或者是persistent app
                boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                        cpi.name, cpi.flags)
                        && isValidSingletonCall(r == null ? callingUid : r.uid,
                                cpi.applicationInfo.uid);
                if (singleton) {//如果是标记UID 为system
                    userId = UserHandle.USER_SYSTEM;
                }
                cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);

                //check caller的权限
                ...


                ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
                cpr = mProviderMap.getProviderByClass(comp, userId);
                boolean firstClass = cpr == null;
                if (firstClass) {
                    final long ident = Binder.clearCallingIdentity();

                    ...

                    try {
                        ApplicationInfo ai =
                            AppGlobals.getPackageManager().
                                getApplicationInfo(
                                        cpi.applicationInfo.packageName,
                                        STOCK_PM_FLAGS, userId);
                        if (ai == null) {
                            Slog.w(TAG, "No package info for content provider "
                                    + cpi.name);
                            return null;
                        }
                        ai = getAppInfoForUser(ai, userId);
                        //创建对象ContentProviderRecord
                        cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
                    } catch (RemoteException ex) {
                        // pm is in same process, this will never happen.
                    } finally {
                        Binder.restoreCallingIdentity(ident);
                    }
                } else if (dyingProc == cpr.proc && dyingProc != null) {
                    cpr = new ContentProviderRecord(cpr);
                    // This is sort of "firstClass"
                    firstClass = true;
                }

                if (r != null && cpr.canRunHere(r)) {
                    //可以运行在caller 进程中,详细见3.4.4节
                    return cpr.newHolder(null);
                }

                //从mLaunchingProviders中查询是否存在该cpr
                final int N = mLaunchingProviders.size();
                int i;
                for (i = 0; i < N; i++) {
                    if (mLaunchingProviders.get(i) == cpr) {
                        break;
                    }
                }

                //当provider并没有处于mLaunchingProviders队列,则启动它
                if (i >= N) {
                    final long origId = Binder.clearCallingIdentity();

                    try {
                        // Content provider is now in use, its package can't be stopped.
                        try {
                            AppGlobals.getPackageManager().setPackageStoppedState(
                                    cpr.appInfo.packageName, false, userId);
                        } catch (RemoteException e) {
                        } catch (IllegalArgumentException e) {
                            Slog.w(TAG, "Failed trying to unstop package "
                                    + cpr.appInfo.packageName + ": " + e);
                        }

                        //查询进程记录ProcessRecord
                        ProcessRecord proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                        if (proc != null && proc.thread != null && !proc.killed) {
                            if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
                                    "Installing in existing process " + proc);
                            if (!proc.pubProviders.containsKey(cpi.name)) {
                                proc.pubProviders.put(cpi.name, cpr);
                                try {
                                    //provider进程启动,发布provider
                                    proc.thread.scheduleInstallProvider(cpi);
                                } catch (RemoteException e) {
                                }
                            }
                        } else {
                            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;
                        //将cpr添加到mLaunchingProviders
                        mLaunchingProviders.add(cpr);
                    } finally {
                        Binder.restoreCallingIdentity(origId);
                    }
                }

                // Make sure the provider is published (the same provider class
                // may be published under multiple names).
                if (firstClass) {
                    mProviderMap.putProviderByClass(comp, cpr);
                }

                mProviderMap.putProviderByName(name, cpr);
                conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
                        stable);
                if (conn != null) {
                    conn.waiting = true;
                }
            }

如果ContentProvider 所在进程没有存在或被kill时:

  • 根据auth 获取ProviderInfo;
  • 权限检查;
  • 当provider 不是运行在system 进程,且系统未准备好,则抛出异常;
  • 当拥有该provider 的用户并没有运行,则直接返回;
  • 根据ComponentName,从AMS.mProviderMap中查询相应的ContentProviderRecord;
  • 当没有添加ContentProviderRecord 时,表示为首次调用,则创建ContentProviderRecord;
  • 当允许运行在调用者的进程中,且ProcessRecord 不为空,则直接返回
  • 当provider 并没有处于LaunchingProviders队列,则启动provider process;
  • 增加引用计数

发布ContentProvider 分两种情况:

  • provider 进程尚未启动;

    对于此场景,system_server 进程调用startProcessLocked 创建provider进程,且attach 到system_server 后,通过binder call 到provider 进程执行ActivityThread.bindApplication 进行发布;

  • provider 进程已启动,但未发布;

    对于此场景,provider 进程已存在且attach 到system_server,但所对应的provider 还没有发布,通过binder call 到provider进程执行ActivityThread.scheduleInstallProvider 进行发布;

3.4.1 scheduleInstallProvider

frameworks/base/core/java/android/app/ActivityThread.java

注意,这里是Provider 进程

public void scheduleInstallProvider(ProviderInfo provider) {
            sendMessage(H.INSTALL_PROVIDER, provider);
        }

消息INSTALL_PROVIDER最终的处理函数为handleInstallProvider

public void handleInstallProvider(ProviderInfo info) {
        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
        try {
            installContentProviders(mInitialApplication, Arrays.asList(info));
        } finally {
            StrictMode.setThreadPolicy(oldPolicy);
        }
    }

3.4.2 bindApplication

frameworks/base/core/java/android/app/ActivityThread.java

注意,这里是provider进程

private void handleBindApplication(AppBindData data) {
        ...

        try {
            ...
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);
                }
            }
            ...

注意bindApplication 是从attachApplicationLocked 中触发,而对于provider 在attach函数中埋下了ANR 的炸弹:

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

消息为 CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG

timeout 为ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS(10s)

这个ANR 的炸弹需要在3.4.3 和之下的函数中拆除,详细看3.4.5节。

3.4.3 installContentProviders

3.4.13.4.2 两小节就是在provider没有运行下的两种场景,但最终调用的都是installContentProvider:

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();
        }
    }

主要两件事:

  • installProvider
  • AMS.publishContentProviders

3.4.4 installProvider

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 {
                final java.lang.ClassLoader cl = c.getClassLoader();
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                if (packageInfo == null) {
                    // System startup case.
                    packageInfo = getSystemContext().mPackageInfo;
                }
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
                          info.name + " from sourceDir " +
                          info.applicationInfo.sourceDir);
                    return null;
                }
                if (DEBUG_PROVIDER) Slog.v(
                    TAG, "Instantiating local provider " + info.name);
                // XXX Need to create the correct context for this provider.
                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 {
            provider = holder.provider;
            if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
                    + info.name);
        }

        ContentProviderHolder retHolder;

        synchronized (mProviderMap) {
            if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
                    + " / " + info.name);
            IBinder jBinder = provider.asBinder();
            if (localProvider != null) {
                ComponentName cname = new ComponentName(info.packageName, info.name);
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                if (pr != null) {
                    if (DEBUG_PROVIDER) {
                        Slog.v(TAG, "installProvider: lost the race, "
                                + "using existing local provider");
                    }
                    provider = pr.mProvider;
                } else {
                    holder = new ContentProviderHolder(info);
                    holder.provider = provider;
                    holder.noReleaseNeeded = true;
                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } else {
                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
                if (prc != null) {
                    if (DEBUG_PROVIDER) {
                        Slog.v(TAG, "installProvider: lost the race, updating ref count");
                    }
                    // We need to transfer our new reference to the existing
                    // ref count, releasing the old one...  but only if
                    // release is needed (that is, it is not running in the
                    // system process).
                    if (!noReleaseNeeded) {
                        incProviderRefLocked(prc, stable);
                        try {
                            ActivityManager.getService().removeContentProvider(
                                    holder.connection, stable);
                        } catch (RemoteException e) {
                            //do nothing content provider object is dead any way
                        }
                    }
                } else {
                    ProviderClientRecord client = installProviderAuthoritiesLocked(
                            provider, localProvider, holder);
                    if (noReleaseNeeded) {
                        prc = new ProviderRefCount(holder, client, 1000, 1000);
                    } else {
                        prc = stable
                                ? new ProviderRefCount(holder, client, 1, 0)
                                : new ProviderRefCount(holder, client, 0, 1);
                    }
                    mProviderRefCountMap.put(jBinder, prc);
                }
                retHolder = prc.holder;
            }
        }
        return retHolder;
    }

代码比较长,逻辑大概分下面几部分:

  • 当holder 为null 或者holder.provider为null 时:
  • 当holder 为null时,是由于AMS.getContentProviderImpl 中一些条件限制;
  • 当holder.provider 为null时,是由于AMS.getContentProviderImpl 中确定provider 可以在调用者的进程中发布,此时holder 不为null;
  • 根据参数ProviderInfo 重新创建、init 一个localProvider(通过函数instantiateProvider)
  • 通过localProvider.getIContentProvider 获取provider 代理,详细见第4节;
  • 如果获取provider 成功,则调用localProvider.attachInfo,进入onCreate 流程
  • 当holder 或holder.provider 都不为null时,表示provider已经创建成功
  • 维护mLocalProvidersByName 和mProviderRefCountMap

attachInfo 这里暂时不分析了,代码比较简单,设置一些Provider 的权限和authority 并最终调用onCreate;

最终通过getIContentProvider 获取的provider 会存放在ContentProviderHolder 里面,这里很重要。可以接着看第4节。

3.4.5 AMS.publishContentProviders

代码比较多,这里不列举,主要做如下:

  • 拆除ANR 的炸弹:
if (wasInLaunchingProviders) {
                        mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                    }
  • 发布成功,notify 到3.5 节中的等待
synchronized (dst) {
                        dst.provider = src.provider;
                        dst.setProcess(r);
                        dst.notifyAll();
                    }

3.5 等待provider 发布

synchronized (cpr) {
            while (cpr.provider == null) {
                ...
                try {
                    final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());

                    if (conn != null) {
                        conn.waiting = true;
                    }
                    cpr.wait(wait);
                    if (cpr.provider == null) {
                        timedOut = true;
                        break;
                    }
                } catch (InterruptedException ex) {
                } finally {
                    if (conn != null) {
                        conn.waiting = false;
                    }
                }
            }
        }

3.4

至此,AMS.getContentProviderImpl 分析基本完成。

4. getIContentProvider

这个函数是在3.4.4 节中触发,在调用query / insert 等接口前首先要通过acquire 获取到provider proxy,最终就是通过getIContentProvider 而来:

frameworks/base/core/java/android/content/ContentProvider.java

public IContentProvider getIContentProvider() {
        return mTransport;
    }
private Transport mTransport = new Transport();

AMS 中在启动provider之后都将provider proxy 存放到ContentProviderRecord 中,并保存在AMS 的providerMap 中。

4.1 Tansport

class Transport extends ContentProviderNative {
abstract public class ContentProviderNative extends Binder implements IContentProvider {
    ...
}

Transport 是继承自ContentProviderNative,看完整个ContentProviderNative,会发现第 3.4.4 节中通过getIContentProvider 接口获取的provider 是Bn provider。即,在provider 的ActivityThread 中获取的provider 为Bn provider

但是,当经过3.4.4节provider 获取后,紧接着会调用3.4.5节进行publish,而在publish 中会将ContentProviderHolder 作为参数传递给AMS。

---->来看下ContentProviderHolder

frameworks/base/core/java/andriod/app/ContentProviderHolder.java

public class ContentProviderHolder implements Parcelable {
    ...

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        info.writeToParcel(dest, 0);
        if (provider != null) {
            dest.writeStrongBinder(provider.asBinder());
        } else {
            dest.writeStrongBinder(null);
        }
        dest.writeStrongBinder(connection);
        dest.writeInt(noReleaseNeeded ? 1 : 0);
    }

    public static final @android.annotation.NonNull Parcelable.Creator<ContentProviderHolder> CREATOR
            = new Parcelable.Creator<ContentProviderHolder>() {
        @Override
        public ContentProviderHolder createFromParcel(Parcel source) {
            return new ContentProviderHolder(source);
        }

        @Override
        public ContentProviderHolder[] newArray(int size) {
            return new ContentProviderHolder[size];
        }
    };

    @UnsupportedAppUsage
    private ContentProviderHolder(Parcel source) {
        info = ProviderInfo.CREATOR.createFromParcel(source);
        provider = ContentProviderNative.asInterface(
                source.readStrongBinder());
        connection = source.readStrongBinder();
        noReleaseNeeded = source.readInt() != 0;
    }
}

(1)ContentProviderHolder 为一个Parcelable,用以binder间数据传递;

(2)ContentProviderHolder.writeToParcel 是将provider.asBinder 写入data;

dest.writeStrongBinder(provider.asBinder());

若,provider 为Bn provier,即为ContentProviderNative,asBinder为自身,即remote 端。

public IBinder asBinder()
    {
        return this;
    }

若,provider 为Bp provider,即为ContentProviderProxy(为什么是这个,后面讲),asBinder 为:

public IBinder asBinder()
    {
        return mRemote;
    }

即,无论provider是Bn provider,还是Bp provider,只要以ContentProviderHolder 传递,就会将Bn provider 作为数据写入。

(3)通过CREATOR读取provider

provider = ContentProviderNative.asInterface(
                source.readStrongBinder());
static public IContentProvider asInterface(IBinder obj)
    {
        if (obj == null) {
            return null;
        }
        IContentProvider in =
            (IContentProvider)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ContentProviderProxy(obj);
    }

总结,如果ContentProviderHolder 作为参数传递,对端永远是ContentProviderProxy。即,provider 进程在创建后,无论是AMS 还是APP 拿到的provider 都是ContentProviderProxy,即都是用proxy 进行通信。

ok,终于解惑了provider 的产生和传递,回到第 2节query 接口,通过acquireUnstableProvider 获取到的provider 就是ContentProviderProxy。剩下来的就都是binder通信的问题啦,最终调用的肯定是Bn provider 中的query ,也就是Transport 中的query 接口,最终调用的当然是ContentProvider 中的query 接口。。。。。

5. 总结

至此,ContentProvider 机制就完整的剖析完了,中间可能过程还有些省略,如果有什么问题,可以留言!

下面做一些总结,大体分:

  • 针对3.3 节当provider 已经运行的场景;
  • 针对3.4节当provider 尚未运行的场景;
  • 整体的调用流程图;
  • ANR 炸弹埋下、拆除说明;

5.1 针对provider 已经运行的场景

PreferencesContentProvider 其他app provider context_provider

  •  Client进程在获取provider的过程,发现cpr为空,则调用scheduleInstallProvider来向provider所在进程发出一个oneway的binder请求,并进入wait()状态.
  • provider进程安装完provider信息,则notifyAll()处于等待状态的进程/线程;

如果provider在publish完成之后, 这时再次请求该provider,那就便没有的最右侧的这个过程,直接在AMS.getContentProviderImpl之后便进入AT.installProvider的过程,而不会再次进入wait()过程.

5.2 针对provider 尚未运行的场景

PreferencesContentProvider 其他app provider context_ContentObserver_02

  •  client进程:通过binder(调用AMS.getContentProviderImpl)向system_server进程请求相应的provider;
  • system进程:如果目标provider所对应的进程尚未启动,system_server会调用startProcessLocked来启动provider进程; 当进程启动完成,此时cpr.provider ==null,则system_server便会进入wait()状态,等待目标provider发布;
  • provider进程:进程启动后执行完attch到system_server,紧接着执行bindApplication;在这个过程会installProvider以及 publishContentProviders;再binder call到system_server进程;
  • system进程:再回到system_server,发布provider信息,并且通过notify机制,唤醒前面处于wait状态的binder线程;并将 getContentProvider的结果返回给client进程;
  • client进程:接着执行installProvider操作,安装provider的(包含对象记录,引用计数维护等工作);

5.3 整体调用流程

PreferencesContentProvider 其他app provider context_ContentResolver_03


5.4 ANR

在3.4.2 节已经说明在启动provider 进程时最终会调用attachApplicationLocked,并埋下ANR 炸弹。timeout 为:

ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS(10s)

在3.4.5 节中通过publish 接口拆除炸弹。其实这个时间都在AMS 和provider ActivityThread 的流程中,我们能做的就是onCreate()时尽可能的少做耗时操作。

OVER~~

参考:

http://gityuan.com/2016/07/30/content-provider/