1. 程式人生 > >Android ContentProvider原理分析

Android ContentProvider原理分析

目錄

  • ContentProvider概述
  • 類圖
  • 時序圖
  • 原始碼解析
    • installProvider
    • ContentResolver中的CURD
    • acquireProvider
    • 到AMS獲取ContentProvider
    • publishContentProvider
    • removeDyingProvider
  • 總結

1. ContentProvider概述

ContentProvider作為Android四大元件之一,重要性肯定是不言而喻的,顧名思義,內容提供者,其最重要的作用就在於提供了一種跨程序獲取資料的方式,provider元件不僅可以自己的程序使用,還可以提供給其他的程序使用,大大方便了不同程序之間的資料交換,本文將詳細介紹provider執行的原理。

:本文基於Android 8.1

2. 類圖

ContentProvider類圖 看起來涉及到的類非常多,一下有種不知道從何看起的感覺,所以這裡對於其中的重點關注部分加上了顏色: 白色:provider執行過程中涉及到的內部類或私有類,一般APP開發過程中不太會涉及 藍色:APP開發過程中經常會接觸到的類 紫色:在system server程序中provider相關的類

3. 時序圖

getContentProvider時序圖 其中白色部分:發起獲取provider的client程序 紅色部分:systemserver程序 藍色部分:提供provider的server程序 時序圖解讀:

  1. 1~4流程是在APP自己的程序(程序A)中,一般來講APP在程序啟動之後初始化時,就會installProvider(流程7~10),如果APP請求的provider在自己程序,那麼到4就能獲取到。
  2. 如果請求的provider另外一個程序(程序B)中,則會觸發程序5~6
  3. 如果程序B不存在則先啟動程序B並installprovider(7~10),告訴AMS之後,由AMS返回給程序A對方的provider資訊(此過程中由程序A發起的請求provider的執行緒會一直等待)
  4. 如果程序B存在則AMS直接返回給程序A對方的provider資訊
  5. 查詢到provider資訊之後,如果需要跨程序呼叫,則通過ContentProviderProxy發起binder call到對端程序執行query 在這其中,AMS充當一箇中間管理員的角色,每個程序在啟動之後需要把自己應該install的providerinstall之後告訴AMS,這樣後面有其他程序請求這個provider的話,AMS可以告訴你所請求的對端的資訊。

4. 原始碼解析

4.1 ActivityThread.installProvider

installProvider,顧名思義就是安裝provider,說的通俗一點就是把APP程序中的provider元件封裝成物件儲存起來,方便使用 在APP的程序啟動的時候,handleBindApplication中會觸發installProvider:

    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        // 此處的provider資訊是在AMS啟動程序時
        // 從manifest收集到的需要install的provider資訊
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        for (ProviderInfo cpi : providers) {
            // 執行installProvider,注意此處的stable引數預設為true
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }
        try {
            // install完成之後,要告訴AMS
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

進一步看一下ActivityThread.installProvider的具體實現

    private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
        // holder為null表示還沒有install過
        if (holder == null || holder.provider == null) {
            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 {
                // 建立context
                    c = context.createPackageContext(ai.packageName,
                            Context.CONTEXT_INCLUDE_CODE);
                } catch (PackageManager.NameNotFoundException e) {
                    // Ignore
                }
            }
            ...
            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                // 通過反射建立provider物件
                localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();
                // 獲取IContentProvider物件,用於跨程序binder call
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
                          info.name + " from sourceDir " +
                          info.applicationInfo.sourceDir);
                    return null;
                }
                // provider的attach,其中最重要的是會執行provider的onCreate
                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;
        }

        // 到這裡,provider的物件建立好了,那麼接下來需要做的就是資料結構的封裝
        // 把provider相關資訊儲存起來
        ContentProviderHolder retHolder;
        // mProviderMap的key時providerKey,value是ProviderClientReocrd
        // 這兩個類主要是封裝了一些provider的基本資訊,可以到上面看一下類圖
        synchronized (mProviderMap) {
            IBinder jBinder = provider.asBinder();
            if (localProvider != null) {
                ComponentName cname = new ComponentName(info.packageName, info.name);
                // mLocalProvidersByName的key是component資訊,value是對應的ProviderClientReocrd
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                if (pr != null) {
                    // 不為空代表install過
                    provider = pr.mProvider;
                } else {
                    // 對於新建立的provider,建立其對應的ContentProviderHolder物件
                    holder = new ContentProviderHolder(info);
                    holder.provider = provider;
                    holder.noReleaseNeeded = true;
                    // install Authorities
                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                    // mLocalProviders的key是IContentProvider的binder物件,value是ProviderClientRecord
                    // 將封裝好的provider放入map中
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } else {
                // mProviderRefCountMap的key是binder物件,value是ProviderRefCount
                // ProviderRefCount中記錄了這個provider的stable和unstable的數量
                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
                if (prc != null) {
                    if (!noReleaseNeeded) {
                        incProviderRefLocked(prc, stable);
                        try {
                            ActivityManager.getService().removeContentProvider(
                                    holder.connection, stable);
                        } catch (RemoteException e) {
                        }
                    }
                } else {
                    ProviderClientRecord client = installProviderAuthoritiesLocked(
                            provider, localProvider, holder);
                    if (noReleaseNeeded) {
                        // 建立ProviderRefCount
                        prc = new ProviderRefCount(holder, client, 1000, 1000);
                    } else {
                        // 根據引數中的stable和unstable建立物件
                        prc = stable
                                ? new ProviderRefCount(holder, client, 1, 0)
                                : new ProviderRefCount(holder, client, 0, 1);
                    }
                    // 放入map
                    mProviderRefCountMap.put(jBinder, prc);
                }
                retHolder = prc.holder;
            }
        }
        return retHolder;
    }

這裡需要提一下其中的getIContentProvider: 返回的是一個Transport物件,而這又是ContentProviderNative的子類,主要作用就是用來binder通訊的 它的建立:在ContentProvider類中 private Transport mTransport = new Transport(); 也就是說在物件建立的時候就預設建立了 然後是ActivityThread.installProviderAuthoritiesLocked

    private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
            ContentProvider localProvider, ContentProviderHolder holder) {
        final String auths[] = holder.info.authority.split(";");
        final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);

        if (provider != null) {
            for (String auth : auths) {
                // 對於一些特殊的auth,允許跨程序binder call
                // Binder.allowBlocking代表允許執行同步的binder call
                switch (auth) {
                    case ContactsContract.AUTHORITY:
                    case CallLog.AUTHORITY:
                    case CallLog.SHADOW_AUTHORITY:
                    case BlockedNumberContract.AUTHORITY:
                    case CalendarContract.AUTHORITY:
                    case Downloads.Impl.AUTHORITY:
                    case "telephony":
                        Binder.allowBlocking(provider.asBinder());
                }
            }
        }
        // 建立ProviderClientRecord
        final ProviderClientRecord pcr = new ProviderClientRecord(
                auths, provider, localProvider, holder);
        for (String auth : auths) {
            // 根據auth和userId建立ProviderKey,放入mProviderMap
            final ProviderKey key = new ProviderKey(auth, userId);
            final ProviderClientRecord existing = mProviderMap.get(key);
            if (existing != null) {
                Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
                        + " already published as " + auth);
            } else {
                mProviderMap.put(key, pcr);
            }
        }
        return pcr;
    }

小結:

  1. 建立了provider物件,其中也建立了IContentProvider物件(Transport)
  2. 建立ContentProviderHolder
  3. 建立ProviderKey
  4. 建立ProviderClientRecord,這是一個provider在client程序中對應的物件
  5. 分別放入mProviderMap/mLocalProviders/mLocalProvidersByName
  6. 建立ProviderRefCount,放入mProviderRefCountMap

這裡先說一下stableunstable

  1. 代表的是client和server的連結,主要取決於獲取provider的傳參,預設情況下,insert/update/delete建立的連結都是stable,而query則是unstable,不過在query的時候如果失敗,還會重新建立stable
  2. stable和unstable最重大的差別在於unstable的情況下,即使對端掛掉了,client也沒關係,但是stable的話,如果對端程序掛掉了,client也會被跟著級聯kill掉。(後面會介紹)

4.2 ContentResolver中的CURD

4.2.1 ContentResolver.insert

    public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
                @Nullable ContentValues values) {
        Preconditions.checkNotNull(url, "url");
        // 請求provider,經過呼叫之後,最終傳參獲取的是stable型別provider
        IContentProvider provider = acquireProvider(url);
        if (provider == null) {
            throw new IllegalArgumentException("Unknown URL " + url);
        }
        try {
            long startTime = SystemClock.uptimeMillis();
            // 執行通過IContentProvider執行insert,其實是發起了binder call到了provider所在程序執行
            Uri createdRow = provider.insert(mPackageName, url, values);
            long durationMillis = SystemClock.uptimeMillis() - startTime;
            maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
            return createdRow;
        } catch (RemoteException e) {
            return null;
        } finally {
            releaseProvider(provider);
        }
    }

ContentResolver.delete:執行provider的delete ContentResolver.update:執行provider的update ContentResolver.call:執行provider的call,這個比較靈活,傳遞的引數比較多,不固定於某一個 這三個方法的實現都跟instert基本類似,此處不再羅列 下面需要重點說一下query:

4.2.2 query

    public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable CancellationSignal cancellationSignal) {
        Preconditions.checkNotNull(uri, "uri");
        // 這裡執行的方法是acquireUnstableProvider
        // 也就是說建立的連結是unstable型別
        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 {
                // 執行query
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        queryArgs, remoteCancellationSignal);
            } catch (DeadObjectException e) {
                // 如果query失敗
                // 因為unstable型別,失敗代表對端掛掉了,那麼重新請求stable型別連結
                unstableProviderDied(unstableProvider);
                stableProvider = acquireProvider(uri);
                if (stableProvider == null) {
                    return null;
                }
                // 再次query
                qCursor = stableProvider.query(
                        mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
            }
            if (qCursor == null) {
                return null;
            }
            qCursor.getCount();
            long durationMillis = SystemClock.uptimeMillis() - startTime;
            final IContentProvider provider = (stableProvider != null) ? stableProvider
                    : acquireProvider(uri);
            final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
            stableProvider = null;
            qCursor = null;
            return wrapper;
        } catch (RemoteException e) {
            ...
        }
    }
  1. query的時候先建立的unstable連結,然後發起了binder call
  2. 如果catchRemoteException,那說明對段程序掛了,此時重新請求stable型別連結把對方程序拉起來,再執行query

4.3 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;
        // 如果不存在則需要向AMS查詢
        try {
            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;
        }
        // 在本程序中installProvider
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

到這裡可能有有一些迷糊,前面已經installProvider過了,為什麼acquireProvider的時候需要再install一次呢? 答案是因為這是兩個不同的程序

  1. 在bindApplication的時候執行的installProvider是提供provider的程序,也就是server程序
  2. 而在此處查詢的則是需要獲取provider的程序,也就是client程序
  3. server程序installProvider的作用是為了自己程序內使用的話在acquireExistingProvider的時候就能查到了,不需要在跨程序到AMS去查詢
  4. client程序installProvider的作用是經過了一次向AMS的查詢之後,客戶端就可以快取起來,這樣就不用每次都向AMS查詢

4.3.1 ActivityThread.acquireExistingProvider

    public final IContentProvider acquireExistingProvider(
            Context c, String auth, int userId, boolean stable) {
        synchronized (mProviderMap) {
            final ProviderKey key = new ProviderKey(auth, userId);
            final ProviderClientRecord pr = mProviderMap.get(key);
            if (pr == null) {
                return null;
            }
            IContentProvider provider = pr.mProvider;
            IBinder jBinder = provider.asBinder();
            if (!jBinder.isBinderAlive()) {
                // 對端程序掛掉了
                handleUnstableProviderDiedLocked(jBinder, true);
                return null;
            }
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                // 增加引用計數
                incProviderRefLocked(prc, stable);
            }
            return provider;
        }
    }

4.4 ActivityManagerService.getContentProvider

    public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
        enforceNotIsolatedCaller("getContentProvider");
        if (caller == null) {
            String msg = "null IApplicationThread when getting content provider "
                    + name;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }
        return getContentProviderImpl(caller, name, null, stable, userId);
    }

簡單對caller進行了判斷,重點在getContentProviderImpl

4.4.1 getContentProviderImpl

方法比較長,刪除了其中一些無用的log以及checkTime

    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
        ContentProviderRecord cpr;
        ContentProviderConnection conn = null;
        ProviderInfo cpi = null;
        synchronized(this) {
            // 對caller判斷

            boolean checkCrossUser = true;
            // 在AMS的providermap中先看看是不是已經存在了
            cpr = mProviderMap.getProviderByName(name, userId);
            // 如果不存在且system,校驗Singleton,也就是隻能有一個
            if (cpr == null && userId != UserHandle.USER_SYSTEM) {
                cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
                if (cpr != null) {
                    cpi = cpr.info;
                    if (isSingleton(cpi.processName, cpi.applicationInfo,
                            cpi.name, cpi.flags)
                            && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
                        userId = UserHandle.USER_SYSTEM;
                        checkCrossUser = false;
                    } else {
                        cpr = null;
                        cpi = null;
                    }
                }
            }
            // 看看這個provider當前是不是已經在運行了
            boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
            // 如果已經在運行了,也就說明各個資料結構都已經新增過了,程序也都已經存在了
            if (providerRunning) {
                cpi = cpr.info;
                if (r != null && cpr.canRunHere(r)) {
                    // canRunHere主要針對自己請求的provider在自己程序中的情況,一般不會遇到
                    ContentProviderHolder holder = cpr.newHolder(null);
                    holder.provider = null;
                    return holder;
                }
                ...

                final long origId = Binder.clearCallingIdentity();
                // 建立provider之間的連結
                conn = incProviderCountLocked(r, cpr, token, stable);
                // 需要更新lru佇列
                if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
                    if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
                        updateLruProcessLocked(cpr.proc, false, null);
                    }
                }
                final int verifiedAdj = cpr.proc.verifiedAdj;
                // 更新程序優先順序
                boolean success = updateOomAdjLocked(cpr.proc, true);
                if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
                    success = false;
                }
                // 記錄usageStates
                maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
                if (!success) {
                    // 更新lru失敗代表那個程序掛掉了
                    boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
                    // 需要處理程序掛掉的流程
                    // 並且標誌provider當前不在執行狀態,這樣會走進下面的provider不在執行的流程中
                    appDiedLocked(cpr.proc);
                    if (!lastRef) {
                        return null;
                    }
                    providerRunning = false;
                    conn = null;
                } else {
                    cpr.proc.verifiedAdj = cpr.proc.setAdj;
                }
                Binder.restoreCallingIdentity(origId);
            }
            // 如果provider當前不在執行
            if (!providerRunning) {
                try {
                    // 先從packageManager那邊查詢到相關的資訊
                    cpi = AppGlobals.getPackageManager().
                        resolveContentProvider(name,
                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
                } catch (RemoteException ex) {
                }
                // 沒查到的話那就說明這個是一個無效的provider
                if (cpi == null) {
                    return null;
                }
                boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                        cpi.name, cpi.flags)
                        && isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
                if (singleton) {
                    userId = UserHandle.USER_SYSTEM;
                }
                // 獲取applicationInfo
                cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);

                String msg;
                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
                        != null) {
                    throw new SecurityException(msg);
                }
                // 在system啟動之前不允許非system的程序獲取provider
                if (!mProcessesReady
                        && !cpi.processName.equals("system")) {
                    throw new IllegalArgumentException(
                            "Attempt to launch content provider before system ready");
                }
                // 校驗user是否正在執行中
                if (!mUserController.isUserRunningLocked(userId, 0)) {
                    Slog.w(TAG, "Unable to launch app "
                            + cpi.applicationInfo.packageName + "/"
                            + cpi.applicationInfo.uid + " for provider "
                            + name + ": user " + userId + " is stopped");
                    return null;
                }

                ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
                // 從getProviderByClass中獲取,看看這個provider是不是AMS這邊已經有儲存了
                // 如果沒有,代表這個provider從來沒有向AMS註冊過
                // 此時需要建立一個新的ContentProviderRecord
                cpr = mProviderMap.getProviderByClass(comp, userId);
                final boolean firstClass = cpr == null;
                if (firstClass) {
                    final long ident = Binder.clearCallingIdentity();
                    if (mPermissionReviewRequired) {
                        if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
                            return null;
                        }
                    }

                    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);
                        cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
                    } catch (RemoteException ex) {
                        // pm is in same process, this will never happen.
                    } finally {
                        Binder.restoreCallingIdentity(ident);
                    }
                }
                ...
                // 看看當前請求的provider是不是在mLaunchingProviders
                // 如果一個 provider被請求過,但是因為對方程序沒有啟動沒有publishProvider
                // 則會加入mLaunchingProviders中
                final int N = mLaunchingProviders.size();
                int i;
                for (i = 0; i < N; i++) {
                    if (mLaunchingProviders.get(i) == cpr) {
                        break;
                    }
                }
                // launching中沒有
                if (i >= N) {
                    final long origId = Binder.clearCallingIdentity();

                    try {
                        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 proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                        if (proc != null && proc.thread != null && !proc.killed) {
                        // 如果程序已經存在,那麼就讓這個程序去installProvider即可
                            if (!proc.pubProviders.containsKey(cpi.name)) {
                                proc.pubProviders.put(cpi.name, cpr);
                                try {
                                    proc.thread.scheduleInstallProvider(cpi);
                                } catch (RemoteException e) {
                                }
                            }
                        } else {
                            // 如果程序不存在,那就得先啟動程序
                            // 因為provider必然執行在某一個程序中,對端程序不在肯定無法獲取provider
                            proc = startProcessLocked(cpi.processName,
                                    cpr.appInfo, false, 0, "content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false, false, false);
                            if (proc == null) {
                                Slog.w(TAG, "Unable to launch app "
                                        + cpi.applicationInfo.packageName + "/"
                                        + cpi.applicationInfo.uid + " for provider "
                                        + name + ": process is bad");
                                return null;
                            }
                        }
                        // 因為需要啟動程序,記錄這個provider的launchingApp
                        // 並把這個provider加入到mLaunchingProviders中,等待對方publish之後
                        // 再從mLaunchingProviders中移除
                        cpr.launchingApp = proc;
                        mLaunchingProviders.add(cpr);
                    } finally {
                        Binder.restoreCallingIdentity(origId);
                    }
                }
                // 如果是第一次建立的provider,還需要放入mProviderMap
                if (firstClass) {
                    mProviderMap.putProviderByClass(comp, cpr);
                }
                mProviderMap.putProviderByName(name, cpr);
                // 建立privier之間的連結
                conn = incProviderCountLocked(r, cpr, token, stable);
                if (conn != null) {
                    conn.waiting = true;
                }
            }
            grantEphemeralAccessLocked(userId, null /*intent*/,
                    cpi.applicationInfo.uid, UserHandle.getAppId(Binder.getCallingUid()));
        }

        // 程式碼執行到這裡就說明當前的provider不存在,需要等待對端publish
        synchronized (cpr) {
            while (cpr.provider == null) {
                ...
                try {
                    if (conn != null) {
                        conn.waiting = true;
                    }
                    // 在需要獲取的provider上wait
                    // 直到對端provider被publish之後,方可notify
                    cpr.wait();
                } catch (InterruptedException ex) {
                } finally {
                    if (conn != null) {
                        conn.waiting = false;
                    }
                }
            }
        }
        return cpr != null ? cpr.newHolder(conn) : null;
    }

這段邏輯比較長,總結來說流程如下:

  1. 校驗caller/許可權等
  2. 如果provider已經在執行,那麼建立連線
  3. 更新程序優先順序,如果更新失敗則代表程序被kill了,標記provider不在執行狀態
  4. provider不在執行狀態,則需要看一下provider是否mProviderMap中曾經有記錄
  5. 如果沒有需要建立新的ContentProviderRecord物件,並加到mProviderMap
  6. 判斷程序是否存在
  7. 如果程序存在則直接排程程序installProvider
  8. 如果程序不存在則需要先啟動程序
  9. 新增mProviderMap以及mLaunchingProviders
  10. 在需要獲取的provider上等待對方notify

4.4.2 incProviderCountLocked

    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 (stable) {
                        conn.stableCount++;
                        conn.numStableIncs++;
                    } else {
                        conn.unstableCount++;
                        conn.numUnstableIncs++;
                    }
                    return conn;
                }
            }
            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;
    }

這個方法相對簡單,所以沒有寫太多註釋,就是根絕provider是否存在以及是否stable,記錄stable和unstable的數量

4.5 ActivityManagerService.publishContentProviders

上面的流程中多次提到程序啟動的時候會installProvider以及publishContentProviders,接下來就來看一下到底是怎麼回事,在前面4.1節中也有提到,在installProvider之後,會通過binder call告訴AMS,publishContentProvider:

    public final void publishContentProviders(IApplicationThread caller,
            List<ContentProviderHolder> providers) {
        if (providers == null) {
            return;
        }
        enforceNotIsolatedCaller("publishContentProviders");
        synchronized (this) {
            final ProcessRecord r = getRecordForAppLocked(caller);
            if (r == null) {
                throw new SecurityException(
                        "Unable to find app for caller " + caller
                      + " (pid=" + Binder.getCallingPid()
                      + ") when publishing content providers");
            }
            final long origId = Binder.clearCallingIdentity();
            final int N = providers.size();
            // 遍歷所有已經安裝的provider
            for (int i = 0; i < N; i++) {
                ContentProviderHolder src = providers.get(i);
                if (src == null || src.info == null || src.provider == null) {
                    continue;
                }
                ContentProviderRecord dst = r.pubProviders.get(src.info.name);
                if (dst != null) {
                    // 將這個providerRecord放入到mProviderMap中
                    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;
                    // 從mLaunchingProviders中移除
                    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);
                    }
                    // notify當前正在等待這個provider唄publish的所有的binder執行緒
                    synchronized (dst) {
                        dst.provider = src.provider;
                        dst.proc = r;
                        dst.notifyAll();
                    }
                    // 更新程序優先順序
                    updateOomAdjLocked(r, true);
                    maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
                            src.info.authority);
                }
            }
            Binder.restoreCallingIdentity(origId);
        }
    }
  1. 把所有的已經install的Provider放入到mProviderMap,簡而言之相當於是註冊,讓AMS知道這個provider已經在運行了
  2. 把provider從從mLaunchingProviders中移除
  3. 通知等待在這個provider上的binder執行緒,那些binder執行緒就可以拿到provider資訊返回各自程序繼續幹活了

4.6 removeDyingProvider

前面多次說了stable與unstable,而且前面基本都是在操作計數,那麼到底什麼時候會用到這個計數呢? 重點就在這個removeDyingProviderLocked,這個方法作用是當一個程序死亡之後,把其中的所有的provider也remove掉:

    private final boolean removeDyingProviderLocked(ProcessRecord proc,
            ContentProviderRecord cpr, boolean always) {
        final boolean inLaunching = mLaunchingProviders.contains(cpr);
        // 如果這個provider還在等待launching就被remove了
        // 需要把當前等待這個provider的執行緒都notifyAll,否則就再也沒有機會notify了
        if (!inLaunching || always) {
            synchronized (cpr) {
                cpr.launchingApp = null;
                cpr.notifyAll();
            }
            // 從mProviderMap中移除
            mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid));
            String names[] = cpr.info.authority.split(";");
            for (int j = 0; j < names.length; j++) {
                mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid));
            }
        }
        // 判斷這個provider上的所有連結
        for (int i = cpr.connections.size() - 1; i >= 0; i--) {
            ContentProviderConnection conn = cpr.connections.get(i);
            if (conn.waiting) {
                if (inLaunching && !always) {
                    continue;
                }
            }
            ProcessRecord capp = conn.client;
            conn.dead = true;
            // 如果stableCount大於0,也就是說存在stable的連結
            // server掛掉了,那麼就會把client也給kill掉
            // 這正是我們前面提到的stable和unstable的重大區別
            if (conn.stableCount > 0) {
                if (!capp.persistent && capp.thread != null
                        && capp.pid != 0
                        && capp.pid != MY_PID) {
                    capp.kill("depends on provider "
                            + cpr.name.flattenToShortString()
                            + " in dying proc " + (proc != null ? proc.processName : "??")
                            + " (adj " + (proc != null ? proc.setAdj : "??") + ")", true);
                }
            } else if (capp.thread != null && conn.provider.provider != null) {
                try {
                    capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
                } catch (RemoteException e) {
                }
                cpr.connections.remove(i);
                if (conn.client.conProviders.remove(conn)) {
                    stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name);
                }
            }
        }
        if (inLaunching && always) {
            mLaunchingProviders.remove(cpr);
        }
        return inLaunching;
    }

5. 總結

經過上面的解讀,主要需要了解到的有以下幾點:

  1. application初始化的時候會installProvider
  2. 向AMS請求provider的時候如果對端程序不存在則請求的那個執行緒需要一直等待
  3. 當對方的程序啟動之後並publish之後,請求provider的執行緒才可返回,所以儘量不要在主執行緒請求provider
  4. 請求provider分為stable以及unsbale,stable型別連結在server程序掛掉之後,client程序會跟著被株連kill
  5. insert/delete/update預設建立stable連結,query預設建立unstable連結,如果失敗會重新建立stable連結
  6. AMS作為一箇中間管理員的身份,所有的provider會向它註冊
  7. 向AMS請求到provider之後,就可以在client和server之間自行binder通訊,不需要再經過systemserver