Framework 原始碼解析知識梳理(6) ContentProvider 原始碼解析

澤毛發表於2017-12-21

一、前言

Framework 原始碼解析知識梳理(5) - startService 原始碼分析 中,我們分析了Service啟動的內部實現原理,今天,我們趁熱打鐵,看一下Android中的四大元件中另一個元件ContentProvider

二、原始碼解析

在分析之前,先上一張整個的流程圖,大家在後面繞暈了以後,可以參考這張圖進行對照:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析

2.1 ContentResolver 獲取過程

在使用ContentProvider來進行資料的增刪改查時,第一步就是要通過getContentResolver(),獲得一個ContentResolver物件,該方法實際上呼叫了基類中的mBase變數,也就是ContextImpl中的getContentResolver()方法,並返回它其中的mContentResolver變數。

    //ContextImpl.java

    @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }
複製程式碼

而該mContentResolver是在ContextImpl的建構函式中初始化的,這其實和我們之前在 外掛化知識梳理(9) - 資源的動態載入示例及原始碼分析 中所分析的getResources()方法返回一個Resources物件的過程類似。

    private ContextImpl(ContextImpl container, ActivityThread mainThread,
            LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
            Display display, Configuration overrideConfiguration, int createDisplayWithId) {
        //...
        mContentResolver = new ApplicationContentResolver(this, mainThread, user);
    }
複製程式碼

這上面的ApplicationContentResolverContentResolver的子類:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析

2.2 簡單的查詢過程

現在,我們以ContentResolver所提供的query方法為例,對ContentProvider的呼叫過程進行一次簡單的走讀:

    public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection,
            @Nullable String selection, @Nullable String[] selectionArgs,
            @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
        Preconditions.checkNotNull(uri, "uri");
        //1.獲取ContentProvider介面。
        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();
                //2.建立取消訊號量。
                remoteCancellationSignal = unstableProvider.createCancellationSignal();
                cancellationSignal.setRemote(remoteCancellationSignal);
            }
            try {
                //3.呼叫IContentProvider的query方法。
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            } catch (DeadObjectException e) {
                //如果發生了異常,那麼銷燬unstableProvider物件,重新獲取一個stableProvider物件。
                unstableProviderDied(unstableProvider);
                stableProvider = acquireProvider(uri);
                //如果stableProvider物件還是為空,那麼直接返回空。
                if (stableProvider == null) {
                    return null;
                }
                //呼叫stableProvider進行查詢。
                qCursor = stableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, 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, selection, sortOrder);

            //用CursorWrapperInner把qCursor包裹起來。
            CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
                    stableProvider != null ? stableProvider : acquireProvider(uri));
            stableProvider = null;
            qCursor = null;
            return wrapper;
        } catch (RemoteException e) {
            // Arbitrary and not worth documenting, as Activity
            // Manager will kill this process shortly anyway.
            return null;
        } finally {
            if (qCursor != null) {
                qCursor.close();
            }
            if (cancellationSignal != null) {
                cancellationSignal.setRemote(null);
            }
            if (unstableProvider != null) {
                releaseUnstableProvider(unstableProvider);
            }
            if (stableProvider != null) {
                releaseProvider(stableProvider);
            }
        }
    }
複製程式碼

我們對上面的流程進行一個簡單的梳理:

  • 通過acquireUnstableProvider獲取一個unstableProvider例項,按字面上的翻譯它是一個不穩定的ContentProvider
  • 通過第一步中獲取的unstableProvider例項進行查詢,如果查詢成功,那麼得到qCursor物件;如果ContentProvider所對應的程式已經死亡,那麼將會釋放unstableProvider物件,再通過呼叫acquireProvider方法重新得到一個stableProvider,它和unstableProvider相同,都是實現了IContentProvider介面,之後在通過它來查詢得到qCursor
  • 把第二步中獲得的qCursorCursorWrapperInner包裹起來,這裡需要注意的是第二個引數,如果是通過unstableProvider查詢得到的qCursor,那麼將需要呼叫acquireProvider,並將返回值傳入。

那麼,我們接下來就要分析通過acquireUnstableProvideracquireProvider獲取IContentProvider的過程。

2.3 IContentProvider 獲取過程

首先,通過acquireUnstableProvider方法根據Uri中的authority欄位,呼叫acquireUnstableProvider(Context c, String auth)方法:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
該方法是由我們前面看到的ApplicationContentResolver所實現的:
Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
可以看到,這裡呼叫了mMainThreadacquireProvider方法,它實際上是一個ActivityThread例項,其實現為:

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

        IActivityManager.ContentProviderHolder holder = null;
        try {
            //如果快取當中沒有,那麼首先通過AMS進行獲取。
            holder = ActivityManagerNative.getDefault().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } catch (RemoteException ex) {
        }
        if (holder == null) {
            Slog.e(TAG, "Failed to find provider info for " + auth);
            return null;
        }

        //根據返回的holder資訊進行安裝。
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }
複製程式碼

這裡,首先會去快取中查詢IContentProvider,如果沒有找到,那麼在呼叫AMS的方法去查詢,獲取一個ContentProviderHolder物件。

2.3.1 呼叫者程式不存在快取的情況

在這種情況下面,會執行兩步操作:

  • 第一步:通過ActivityManagerService獲取ContentProviderHolder
  • 第二步:通過返回的ContentProviderHolder中的資訊進行安裝

第一步,通過 ActivityManagerService 獲取 ContentProviderHolder

這裡我們先假設沒有快取的情況,通過 Framework 原始碼解析知識梳理(1) - 應用程式與 AMS 的通訊實現 中學到的知識,我們知道它最終會呼叫到ActivityManagerService的下面這個方法:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
接下來最終會呼叫到getContentProviderImpl方法返回一個ContentProviderHolder物件,這個方法比較長,就不貼程式碼了,直接說結論,這裡會分為以下幾種情況:

(a) ContentProvider 所在程式已經啟動,並且已經該 ContentProvider 已經被安裝

這種情況下,直接返回該ContentProviderHolder即可:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
(b) ContentProvider 所在程式已經啟動,但是該 ContentProvider 沒有被安裝

此時,就需要通過ApplicationThread物件,再和ContentProvider所在的程式進行互動,以返回一個ContentProviderHolder例項:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
經過Binder通訊,那麼最終會呼叫到ContentProvider所在程式的下面這個方法:
Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
這裡面呼叫有呼叫了內部的installContentProviders方法:
Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
這裡的操作分為兩步:

  • 安裝:根據傳過來的List<ProviderInfo>物件,通過installProvider方法進行安裝,並將結果存放在List<ContentProviderHolder>列表中。
  • 釋出:將安裝的結果,再通過一次訊息傳遞,返回給ActivityManagerService

(b-1) 安裝過程

在這一步當中,傳入的第二個引數holdernull,因此會根據Provider的名字,動態地載入該類,並呼叫它的attachInfo方法:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
我們上面的有兩個Provider

  • localProvider,型別為ContentProvider
  • provider,型別為Transport

provider是通過localProvidergetIContentProvider方法獲得的,它是ContentProvider的一個內部類,它的作用就是作為ContentProvider在遠端呼叫者中的一個代理物件,也就是說,ContentProvider的使用者是通過獲取ContentProvider所在程式的一個代理類Transport,再通過這個Transport物件呼叫到ContentProvider進行查詢的:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
接下來,還會去呼叫localProviderattachInfo方法,這裡面會初始化許可權相關的資訊,最終會執行ContentProvideronCreate()方法:
Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
假設上面我們獲得的localProvider不為空,那麼會執行下面的邏輯:
Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
這裡面,我們會生成一個ProviderClientRecord物件,其內部包含了下面幾個變數:
Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析

  • mNamesContentProvider物件的authority
  • mProvider:遠端代理物件
  • mLocalProvider:本地物件
  • mHolder:返回給AMS的資料結構,AMS再會把它返回給ContentProvider的呼叫者,mHolder的型別為IActivityManager.ContentProviderHolder,其內部包含的資料結構為:
    Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析

關於ContentProviderHolderProviderClientRecord,其繼承族譜如下圖所示:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
(b-2) 釋出過程

釋出過程,其實就是呼叫了ActivityManagerServicepublishContentProviders方法,將在ContentProvider擁有者所建立的List<ContentProviderHolder>儲存起來:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析

(c) ContentProvider 所在程式沒有啟動

在這種情況下,就需要先通過startProcessLocked啟動ContentProvider所在程式,等待程式啟動完畢之後,再進行安裝。

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析

第二步,利用返回的 ContentProviderHolder 中的資訊,進行安裝

在第一步中,通過ActivityManagerService,我們最終獲得了ContentProviderHolder物件,接下來就是呼叫installProvider方法,這裡和我們之前在第一步中的(b-1)中所看到的installProvider其實是同一個方法,區別在於,之前我們分析的installProvider傳入的holder引數為空,下面,我們就來看一下當holder引數不為空時最終會走到下面的邏輯:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析
installProviderAuthoritiesLocked方法中,會將它快取在mProviderMap當中。
Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析

2.3.2 呼叫者程式存在快取的情況

當呼叫者程式存在快取時,會呼叫acquireExistingProvider方法,這裡面就會通過我們前面所看到的mProviderMap進行查詢:

Framework 原始碼解析知識梳理(6)   ContentProvider 原始碼解析

三、小結

這篇文章拖了一個星期,總算是完成了,原始碼看的真的頭暈,其實最終看下來,發現整個呼叫過程,和我們之前分析過的 Framework 原始碼解析知識梳理(5) - startService 原始碼分析 很類似,究其根本,就是呼叫者程式、所有者程式和ActivityManagerService程式的三方呼叫。


更多文章,歡迎訪問我的 Android 知識梳理系列:

相關文章