四大組件之ContentProviderRecord

一. 引言

作爲四大組件之一的ContentProvider,相比來說是設計得稍遜色,有些地方不太合理,比如provider級聯被殺, 請求provider時佔用system_server的binder線程來wait()等。

即便很少自定義ContentProvider,但你也可以會需要使用到ContentProvider,比如通信錄,Settings等; 使用Provider往往跟數據庫結合起來使用,所以這裏需要注意不要再主線程用provider做過多的io操作。

二. ContentProvider數據結構

先以一幅圖來展示AMS管理ContentProvider所涉及的相關數據結構: 點擊查看大圖

content_provider_record

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使用過程

點擊查看大圖

Seq_provider

更多源碼詳細過程,見理解ContentProvider原理

以上博文轉自gityuan的四大組件之ContentProviderRecord

 

------------------------------------------------------分割線--------------------------------------------------

以下是本人的一些備註:

ContentProviderRecord創建於ActivityThread調用AMS#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;
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章