四大组件之ContentProviderRecord

一. 引言

作为四大组件之一的ContentProvider,相比来说是设计得稍逊色,有些地方不太合理,比如provider级联被杀, 请求provider时占用system_server的binder线程来wait()等。

即便很少自定义ContentProvider,但你也可以会需要使用到ContentProvider,比如通信录,Settings等; 使用Provider往往跟数据库结合起来使用,所以这里需要注意不要再主线程用provider做过多的io操作。

二. ContentProvider数据结构

先以一幅图来展示AMS管理ContentProvider所涉及的相关数据结构: 点击查看大图

2.1 ContentProviderRecord

  • provider:在ActivityThread的installProvider()过程,会创建ContentProvider对象, 该对象有一个成员变量Transport,继承于ContentProviderNative对象,作为binder服务端。经过binder传递到system_server 进程的便是ContentProvider.Transport的binder代理对象, 由publishContentProviders()过程完成赋值;
  • proc:记录provider所在的进程,是在publishContentProviders()过程完成赋值;
  • launchingApp:记录等待provider所在进程启动,getContentProviderImpl()过程执行创建进程之后赋值;
  • connections:记录该ContentProvider的所有连接信息,
    • 添加连接过程:incProviderCountLocked
    • 减少连接过程:decProviderCountLocked,removeDyingProviderLocked,cleanUpApplicationRecordLocked;
  • externalProcessTokenToHandle: 数据类型为HashMap<IBinder, ExternalProcessHandle>.
    • AMS.getContentProviderExternalUnchecked()过程会添加externalProcessTokenToHandle值;
    • CPR.hasConnectionOrHandle()或hasExternalProcessHandles()都会判断该变量是否为空.

2.2 ContentProviderConnection

功能:连接contentProvider与请求该provider所对应的进程

  1. provider:目标provider所对应的ContentProviderRecord结构体;
  2. client:请求该provider的客户端进程;
  3. waiting:该连接的client进程正在等待该provider发布

2.3 ProcessRecord

  • pubProviders: ArrayMap<String, ContentProviderRecord>
    • 记录当前进程所有已发布的provider;
  • conProviders: ArrayList
    • 记录当前进程跟其他进程provider所建立的连接

2.4 AMS

  • mProviderMap记录系统所有的provider信息;
  • mLaunchingProviders记录当前正在启动的provider;

2.5 ActivityThread

  • mProviderMap: 记录App端的所有provider信息;
  • mProviderRefCountMap:记录App端的所有provider引用信息;

三. Provider使用过程

点击查看大图

更多源码详细过程,见理解ContentProvider原理

以上博文转自gityuan的四大组件之ContentProviderRecord

------------------------------------------------------分割线--------------------------------------------------

以下是本人的一些备注:

ActivityThread中的相关成员:

mProviderMap:ProviderKey由ContentProvider的authorith和userId组成;mProviderMap里面包含了acquireProvider得来的Transport,也包含和从本进程publish的ContentProvider。就是说ProviderClientRecord是本地Provider和从远端进程获得的Provider共用的一个结构体,即ProviderClientRecord可以表示一个本地Provider,也可以表示从远端进程获得的Provider(后面会称为远端Provider)。如果表示从远端进程获得的Provider,那么ContentProvider mLocalProvider成员为null。ContentProviderHolder是两者都有的一个成员。但是ContentProviderHolder#ContentProviderConnection只有表示从远端进程获得的Provider才不为null。

mProviderRefCountMap:是专门用于对远端Provider计数的。

mLocalProviders和mLocalProvidersByName:两者都只是包含本地Provider,两者的区别只在于key不一样。一个是IContentProvider作为key,一个是ComponentName作为key。

ContentProviderRecord创建于ActivityThread调用AMS#attachApplication时(如果Client acquireProvider()时,ContentProviderRecord没创建,则会创建,那么attachApplication时就不会重复创建),而installContentPrivders是在AMS调用IApplicationThread#bindApplication时,在AT中执行的。

AMS#attachApplication

List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;

generateApplicationProvidersLocked

    private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
        List<ProviderInfo> providers = null;
        try {
            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) {
        }
        if (DEBUG_MU) Slog.v(TAG_MU,
                "generateApplicationProvidersLocked, app.info.uid = " + app.uid);
        int userId = app.userId;
        if (providers != null) {
            int N = providers.size();
            app.pubProviders.ensureCapacity(N + app.pubProviders.size());
            for (int i=0; i<N; i++) {
                // TODO: keep logic in sync with installEncryptionUnawareProviders
                ProviderInfo cpi =
                    (ProviderInfo)providers.get(i);
                boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                        cpi.name, cpi.flags);
                if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) {
                    // This is a singleton provider, but a user besides the
                    // default user is asking to initialize a process it runs
                    // in...  well, no, it doesn't actually run in this process,
                    // it runs in the process of the default user.  Get rid of it.
                    providers.remove(i);
                    N--;
                    i--;
                    continue;
                }

                ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
                ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
                if (cpr == null) {
                    cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
                    mProviderMap.putProviderByClass(comp, cpr);
                }
                if (DEBUG_MU) Slog.v(TAG_MU,
                        "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
                app.pubProviders.put(cpi.name, cpr);
                if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
                    // Don't add this if it is a platform component that is marked
                    // to run in multiple processes, because this is actually
                    // part of the framework so doesn't make sense to track as a
                    // separate apk in the process.
                    app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode,
                            mProcessStats);
                }
                notifyPackageUse(cpi.applicationInfo.packageName,
                                 PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
            }
        }
        return providers;
    }

ActivityThread#handleBindApplication(AppBindData data)

            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }

ContentProviderConnection在应用进程向AMS获取IContentProvider时创建

getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId)

conn = incProviderCountLocked(r, cpr, token, stable);

 ContentProviderConnection incProviderCountLocked(ProcessRecord r,
            final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable)

        if (r != null) {
            for (int i=0; i<r.conProviders.size(); i++) {
                ContentProviderConnection conn = r.conProviders.get(i);
                if (conn.provider == cpr) {
                    if (DEBUG_PROVIDER) Slog.v(TAG_PROVIDER,
                            "Adding provider requested by "
                            + r.processName + " from process "
                            + cpr.info.processName + ": " + cpr.name.flattenToShortString()
                            + " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
                    if (stable) {
                        conn.stableCount++;
                        conn.numStableIncs++;
                    } else {
                        conn.unstableCount++;
                        conn.numUnstableIncs++;
                    }
                    return conn;//若Connection已存在,则count++后返回,否则创建一个
                }
            }
//若Connection不存在则创建一个ProcessRecord与该ContentProvider的连接
            ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
            if (stable) {
                conn.stableCount = 1;
                conn.numStableIncs = 1;
            } else {
                conn.unstableCount = 1;
                conn.numUnstableIncs = 1;
            }
            cpr.connections.add(conn);
            r.conProviders.add(conn);
            startAssociationLocked(r.uid, r.processName, r.curProcState,
                    cpr.uid, cpr.name, cpr.info.processName);
            return conn;
        }
        cpr.addExternalProcessHandleLocked(externalProcessToken);
        return null;
    }

文章一开始说被client在AMS调起的binder线程在等待另一binder线程。那么是怎么等待和唤醒的呢,看流程可以知道。在getContentProviderImpl中wait,而在publishContentProviders中notify,具体代码如下:

AMS#getContentProviderImpl()

        // Wait for the provider to be published...
        synchronized (cpr) {
            while (cpr.provider == null) {
                if (cpr.launchingApp == null) {
                    Slog.w(TAG, "Unable to launch app "
                            + cpi.applicationInfo.packageName + "/"
                            + cpi.applicationInfo.uid + " for provider "
                            + name + ": launching app became null");
                    EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,
                            UserHandle.getUserId(cpi.applicationInfo.uid),
                            cpi.applicationInfo.packageName,
                            cpi.applicationInfo.uid, name);
                    return null;
                }
                try {
                    if (DEBUG_MU) Slog.v(TAG_MU,
                            "Waiting to start provider " + cpr
                            + " launchingApp=" + cpr.launchingApp);
                    if (conn != null) {
                        conn.waiting = true;
                    }
                    cpr.wait();//Object.wait(),在这里等待provider发布
                } catch (InterruptedException ex) {
                } finally {
                    if (conn != null) {
                        conn.waiting = false;
                    }
                }
            }
        }

AMS#publishContentProviders()

ContentProviderRecord dst = r.pubProviders.get(src.info.name);
                if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
                if (dst != null) {
                    ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
                    mProviderMap.putProviderByClass(comp, dst);
                    String names[] = dst.info.authority.split(";");
                    for (int j = 0; j < names.length; j++) {
                        mProviderMap.putProviderByName(names[j], dst);
                    }

                    int launchingCount = mLaunchingProviders.size();
                    int j;
                    boolean wasInLaunchingProviders = false;
                    for (j = 0; j < launchingCount; j++) {
                        if (mLaunchingProviders.get(j) == dst) {
                            mLaunchingProviders.remove(j);
                            wasInLaunchingProviders = true;
                            j--;
                            launchingCount--;
                        }
                    }
                    if (wasInLaunchingProviders) {
                        mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                    }
                    synchronized (dst) {
                        dst.provider = src.provider;
                        dst.proc = r;
                        dst.notifyAll();//Object.notifyAll(),唤醒在等待队列中的binder线程
                    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值