1. 程式人生 > >Android System Server大綱之ContentService和ContentProvider原理剖析

Android System Server大綱之ContentService和ContentProvider原理剖析

前言

ContentService是Android四大元件之一的ContentProvider密切相關的一個系統服務,且和AccountManagerService也有著緊密的聯絡。ContentService以功能來劃分,就兩大模組:

  • 提供管理通知監聽ContentProvider資料變化的觀察者的服務
  • 同步功能:同步日曆、Email等

第一個功能,對於很多開發者來說可能熟悉。而對於第二個,可能就會陌生一點,這個是Android提供的同步功能,很多APP開發者,也很難用到這個Android的同步功能。ContentService的同步功能和AccountManagerService往往緊密聯絡在一起。本文將不再闡述ContentService的同步功能,有興趣的讀者可以自行閱讀Android的參考文件瞭解該功能的使用方法

https://developer.android.google.cn/reference/android/content/SyncRequest.html。那麼本文將闡述的內容是:

  • ContentProvider的原理
  • ContentProvider的觀察者服務

ContentProvider是Android的四大元件之一,是提供APP間安全共享資料的方式之一,APP可以通過ContentProvider從其它APP中獲取到想要的資料,例如讀取手機的聯絡人。也可以通過ContentProvider把APP的資料共享出去給其它APP使用。

以查詢ContentProvider的資料的流程為線索,一步一步分析這個過程,從而瞭解ContentProvider的過程和原理。

APP發起查詢

引用官方的一段話:如果您想要訪問內容提供程式中的資料,可以將應用的 Context 中的 ContentResolver 物件用作客戶端來與提供程式通訊。 ContentResolver 物件會與提供程式物件(即實現 ContentProvider 的類例項)通訊。 提供程式物件從客戶端接收資料請求,執行請求的操作並返回結果。

APP發起查詢,很簡單,如下:

ContentResolver cr = getContentResolver();
cr.query(Uri uri, ....);
  • 首先通過getContentResolver()獲取到ContentResolver物件,ContentResolver物件好比內容分解器,能夠分解出來需要呼叫哪個內容提供程式即ContentProvider。ContentResolver分解的依據便是引數Uri。Uri是如何把ContentResolver和ContentProvider聯絡起來的呢?Uri可以看作是一個地址描述,隨意舉一個Uri可描述的地址例子:

content://user_dictionary

可以和平時上網的網址做一個類比,如訪問百度:https://www.baidu.com

Uri和網址結構有很大相似性,https表示的是網路技術中的傳輸協議,那麼content也可以當做ContentProvider中的一種協議,那麼content就代表當前需要通過ContentProvider進行資料通訊。www.baidu.com是百度的域名,表示需要訪問網站的位置,user_dictionary也就可以比作ContentProvider的域名,user_dictionary表示是那個ContentProvider。換句話說,ContentResolver接收到content://user_dictionary這個Uri時,就知道當前需要發起ContentProvider資料互動,通過user_dictionary可以尋找用user_dictionary表示的ContentProvider。

深入ContentResolver

準備工作

檢視ContentResolver的定義,如下:

public abstract class ContentResolver {
    ......
}
  • 可見ContentResolver是一個抽象類,所以說getContentResolver()所獲取到例項本質是ContentResolver的子類,檢視getContentResolver()的具體實現:
private final ApplicationContentResolver mContentResolver;
public ContentResolver getContentResolver() {
    return mContentResolver;
}
  • 上面的程式碼定義在檔案frameworks/base/core/java/android/app/ContextImpl.java中

從上面的程式碼可知,getContentResolver()獲取到的實際是ApplicationContentResolver的例項mContentResolver。cr.query(Uri uri, ….)的實現如下:

public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,......) {
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    ......
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        ......
        try {
            qCursor = unstableProvider.query(mPackageName, uri, projection,
                    selection, selectionArgs, sortOrder, remoteCancellationSignal);
        } catch (DeadObjectException e) {
            unstableProviderDied(unstableProvider);
            stableProvider = acquireProvider(uri);
            if (stableProvider == null) {
                return null;
            }
            qCursor = stableProvider.query(mPackageName, uri, projection,
                    selection, selectionArgs, sortOrder, remoteCancellationSignal);
        }
        if (qCursor == null) {
            return null;
        }
        ......
}
  • 這個方法定義在檔案frameworks/base/core/java/android/content/ContentResolver.java中。

這裡用文字描述一下上面程式碼的過程,先宣告兩個相同型別的區域性變數unstableProvider、stableProvider,都是IContentProvider的例項,IContentProvider是AIDL的介面,所以IContentProvider所呼叫的方法是遠端呼叫。通過acquireUnstableProvider()方法給unstableProvider變數賦值,unstableProvider呼叫query()方法,如果發生DeadObjectException異常,呼叫acquireProvider()方法給stableProvider賦值,然後呼叫stableProvider的query()方法。

這裡有點奇怪,為什麼要宣告兩個一樣的IContentProvider變數,且呼叫相同的方法query()。從變數的命名上,也能找到一點線索,unstableProvider和stableProvider,前者是不穩定的provider,後者是穩定的provider。什麼樣表示穩定的,什麼樣表示不穩定的?從上面的程式碼來看,不穩定的provider,呼叫query()方法會丟擲DeadObjectException。下面先看看unstableProvider的賦值過程:

public final IContentProvider acquireExistingProvider(......) {
    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();
        ......
        return provider;
    }
}
  • 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。

可見unstableProvider是從變數mProviderMap的例項中遍歷出來,也就是說,在呼叫者的程序內部,對provider會快取到mProviderMap這個變數中。而IContentProvider是一個跨程序的遠端呼叫。換句話說,呼叫者的程序內部所快取的unstableProvider例項所對應的遠端程序不能保證還正常執行著,如果所對應的遠端程序沒有執行,那麼就會丟擲DeadObjectException異常,並呼叫unstableProviderDied()方法把本地的快取清除。然後通過acquireProvider()重新生產一個新的stableProvider,且Android系統會啟動和stableProvider對應的遠端程序,這個過程下文會闡述。可見Android這個舉措是為了效能考慮,目的讓APP的執行僅可能的快。

獲取ContentProvider

回到上文中例項化變數unstableProvider和stableProvider的程式碼,unstableProvider或stableProvider例項化後,直接呼叫query()方法,然後直接就可以返回Cursor給呼叫者了,說明在這裡的query()這一步,已經拿到呼叫者需要的資料。所以理解unstableProvider和stableProvider的實質至關重要。那麼unstableProvider和stableProvider所對應的遠端程序的服務是哪一個呢?是不是就直接對應ContentProvider呢?

由於unstableProvider是一個呼叫者程序中快取的例項,所以需要對stableProvider下手,瞭解stableProvider的例項化過程即可,如下:

public final IContentProvider acquireProvider(......) {
    ......
    IActivityManager.ContentProviderHolder holder = null;
    try {
        holder = ActivityManagerNative.getDefault().getContentProvider(
                getApplicationThread(), auth, userId, stable);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    ......
    return holder.provider;
}
  • 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。

stableProvider被封裝到ContentProviderHolder的變數中provider中。通過呼叫ActivityManagerNative.getContentProvider()獲取ContentProviderHolder,ActivityManagerNative的所對應的遠端服務是system_process中的ActivityManagerService,接著看getContentProvider()這個方法:

public final ContentProviderHolder getContentProvider(
        IApplicationThread caller, String name, int userId, boolean stable) {
    ......
    return getContentProviderImpl(caller, name, null, stable, userId);
}
  • 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。

這裡直接呼叫了getContentProviderImpl()方法:

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, boolean stable, int userId) {
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;

    synchronized(this) {
        ......

        boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
        if (providerRunning) {
            ......
        }

        if (!providerRunning) {
            ......
            // If the provider is not already being launched, then get it
            // started.
            if (i >= N) {

                try {
                    // Content provider is now in use, its package can't be stopped.
                    ......
                    if (proc != null && proc.thread != null && !proc.killed) {
                        ......
                    } else {
                        proc = startProcessLocked(cpi.processName,
                                cpr.appInfo, false, 0, "content provider",
                                new ComponentName(cpi.applicationInfo.packageName,
                                        cpi.name), false, false, false);
                       ......
                    }
                    cpr.launchingApp = proc;
                    mLaunchingProviders.add(cpr);
                } finally {
                    Binder.restoreCallingIdentity(origId);
                }
            }
            ......

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

    // Wait for the provider to be published...
    synchronized (cpr) {
        while (cpr.provider == null) {
            ......
                cpr.wait();
            ......
        }
    }
    return cpr != null ? cpr.newHolder(conn) : null;
}
  • 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。

由於這個方法的程式碼篇幅非常大,這裡省略了很多程式碼,省略的程式碼主要就是一些快取、許可權等等相關的程式碼。這裡假設provider的程序沒有啟動,從最原始,最乾淨的時候分析ContentProvider的這個過程。聚焦上面的程式碼,首先分兩種情況,providerRunning和!providerRunning,providerRunning是基於!providerRunning為前提的,因此provider第一次啟動是執行!providerRunning這個程式碼路徑。假若provider沒有執行過,Android首先通過startProcessLocked()把provider所在的應用啟動起來,這個過程這裡暫時先放下,繼續往下看,在最後面,有一個while迴圈,迴圈條件是cpr.provider == null?這裡會一直等待cpr的provider變數被賦值,回到上文中的acquireProvider()方法的程式碼,acquireProvider()首先要獲取的是ContentProviderHolder的例項,上面的程式碼如果cpr != null,就返回一個ContentProviderHolder物件(cpr.newHolder(conn))。所以這裡的cpr.provider就是上文中acquireProvider()需要獲取的IContentProvider的例項物件。

公佈ContentProvider

因此,只要繼續分析清楚cpr.provider的賦值過程,就知道開始的時候ContentResolver.query()所呼叫的真正的遠端服務。回到上面的程式碼,startProcessLocked()啟動應用時,執行到如下程式碼(APP的啟動過程本文不再贅述):

public static void main(String[] args) {
    ......
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    ......
}
  • 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。

生成ActivityThread例項,也就是常說的主執行緒(UI執行緒了),然後呼叫attach()方法:

private void attach(boolean system) {
    ......
    final IActivityManager mgr = ActivityManagerNative.getDefault();
    try {
        mgr.attachApplication(mAppThread);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    ......
}
  • 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。

又回到ActivityManagerService了,接著往下看:

public final void attachApplication(IApplicationThread thread) {
    ......
    attachApplicationLocked(thread, callingPid);
    ......
}
  • 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。

這裡沒有需要看的程式碼,繼續往下跟蹤:

 private final boolean attachApplicationLocked(IApplicationThread thread,.....) {
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
    thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,......);
}
  • 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。

IApplicationThread例項thread是遠端APP的遠端呼叫端,所以thread.bindApplication()實際又從system_process程序回到了APP所在的程序,注意第三個引數providers,providers是封裝了應用的所有的ContentProvider,詳情讀者可以閱讀generateApplicationProvidersLocked(app)方法,本文不再闡述。繼續往下看:

public final void bindApplication(String processName, ApplicationInfo appInfo,
        List<ProviderInfo> providers, ComponentName instrumentationName,......) {
    ......
    data.providers = providers;
    data.compatInfo = compatInfo;
    data.initProfilerInfo = profilerInfo;
    sendMessage(H.BIND_APPLICATION, data);
}
  • 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。

傳送了一個H.BIND_APPLICATION的訊息,注意data.providers = providers。該訊息的處理是在:

private void handleBindApplication(AppBindData data) {
if (!data.restrictedBackupMode) {
    if (!ArrayUtils.isEmpty(data.providers)) {
        installContentProviders(app, data.providers);
        ......
    }
}
  • 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。

呼叫了installContentProviders()方法,注意引數data.providers,如下:

private void installContentProviders(
        Context context, List<ProviderInfo> providers) {
    final ArrayList<IActivityManager.ContentProviderHolder> results =
        new ArrayList<IActivityManager.ContentProviderHolder>();

    for (ProviderInfo cpi : providers) {
        IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
                false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
        if (cph != null) {
            cph.noReleaseNeeded = true;
            results.add(cph);
        }
    }

    try {
        ActivityManagerNative.getDefault().publishContentProviders(
            getApplicationThread(), results);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}
  • 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。

把應用的所有的ContentProvider資訊封裝到ContentProviderHolder中,這裡要回到上文中acquireProvider()方法,獲取IContentProvider的例項正好是ContentProviderHolder的中的變數provider,因此,這裡需要了解上面程式碼ContentProviderHolder的例項cph的例項化過程,看installProvider()方法:

private IActivityManager.ContentProviderHolder installProvider(Context context,
        IActivityManager.ContentProviderHolder holder, ProviderInfo info,.....) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) {
        ......
        try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
            ......

    IActivityManager.ContentProviderHolder retHolder;

    synchronized (mProviderMap) {
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
            ComponentName cname = new ComponentName(info.packageName, info.name);
            ProviderClientRecord pr = mLocalProvidersByName.get(cname);
            if (pr != null) {
                ......
            } else {
                holder = new IActivityManager.ContentProviderHolder(info);
                holder.provider = provider;
                ......
            }
            retHolder = pr.mHolder;
        } else {
            ......

    return retHolder;
}
  • 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。

上面的程式碼通過ClassLoader對映ContentProvider的物件例項,然後呼叫getIContentProvider()方法,獲取到IContentProvider的例項provider,然後賦值給ContentProviderHolder的例項holder的變數provider,所以provider正是acquireProvider()中獲取的的IContentProvider的例項,而繼續看getIContentProvider()方法檢視provider的本質是:

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

class Transport extends ContentProviderNative {
    public Cursor query(.....){
        ......
    }
    ......
}
......
  • 這個程式碼定義在檔案frameworks/base/core/java/android/content/ContentProvider.java中。

上面的程式碼可見,provider實質是Transport的例項mTransport,Transport是ContentProviderNative的子類,而ContentProviderNative是IContentProvider的子類。

下面繼續看看provider和ActivityManagerService的關係,回到上文中的installContentProviders()方法,呼叫了publishContentProviders()方法,如下:

public final void publishContentProviders(IApplicationThread caller,
        List<ContentProviderHolder> providers) {
    ......
    final int N = providers.size();
    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 (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);
            }
            ......
            synchronized (dst) {
                dst.provider = src.provider;
                dst.proc = r;
                dst.notifyAll();
            }
        }
    }
}
  • 這個程式碼定義在檔案frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。

把應用的ContentProvider資訊快取到mProviderMap中,這裡重點關注語句dst.provider = src.provider,回到上文中getContentProviderImpl()方法中的程式碼,後面的while迴圈,等待cpr.provider的賦值,dst.provider = src.provider正好就是這個賦值過程,cpr.provider賦值後,getContentProviderImpl()返回ContentProviderHolder的例項cpr.newHolder(conn),而這個例項持有變數provider指向Transport的例項mTransport,也就是應用的ContentProvider。

關係圖

這個過程非常繞亂人的頭緒,下面把這個過程簡單轉換成一張簡單的關係圖,理清這個過程和關係,如下:

這裡寫圖片描述

ContentProvider資料監聽

觀察者註冊過程

有時APP開發的時候,APP執行時,需要實時監聽一些ContentProvider資料的變化。如使用者添加了一個聯絡人,APP需要接收到聯絡人新增的通知。由於ContentProvider的提供者,和需求者不是同一個APP,所以,這個通知的紐帶,就需要ContentService來完成。從註冊一個觀察者開始,分析這個通知的來龍去脈,註冊程式碼如下:

ContentResolver cr = getContentResolver();
cr.registerContentObserver(@NonNull Uri uri, boolean notifyForDescendants,
            @NonNull ContentObserver observer);
  • 當ContentProvider的資料發生變化時,便會回撥到ContentObserver的onChange()方法,觀察者ContentObserver observer被註冊到哪裡去呢?繼續進入分析:
public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
        ContentObserver observer, @UserIdInt int userHandle) {
    try {
        getContentService().registerContentObserver(uri, notifyForDescendents,
                observer.getContentObserver(), userHandle);
    } catch (RemoteException e) {
    }
}
  • 這個方法定義在檔案frameworks/base/core/java/android/content/ContentResolver.java中。

上面的程式碼getContentService()獲取到的物件是:

public static IContentService getContentService() {
    if (sContentService != null) {
        return sContentService;
    }
    IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
    if (false) Log.v("ContentService", "default service binder = " + b);
    sContentService = IContentService.Stub.asInterface(b);
    if (false) Log.v("ContentService", "default service = " + sContentService);
    return sContentService;
}
  • 這個方法定義在檔案frameworks/base/core/java/android/content/ContentResolver.java中。

閱讀《Android系統之System Server大綱 》可知,啟動的CONTENT_SERVICE_NAME的系統服務如下:

private static final String CONTENT_SERVICE_CLASS =
            "com.android.server.content.ContentService$Lifecycle";
traceBeginAndSlog("StartContentService");
mSystemServiceManager.startService(CONTENT_SERVICE_CLASS);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
  • 這個方法定義在檔案frameworks/base/services/java/com/android/server/SystemServer.java中。

所以getContentService()獲取到的是ContentService,繼續往下分析registerContentObserver():

public void registerContentObserver(Uri uri, boolean notifyForDescendants,
                                    IContentObserver observer, int userHandle) {

    ......

    synchronized (mRootNode) {
        mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
                uid, pid, userHandle);
        if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
                " with notifyForDescendants " + notifyForDescendants);
    }
}
  • 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/content/ContentService.java中。

上面的程式碼把觀察者observer通過addObserverLocked放置在mRootNode物件中

private void addObserverLocked(Uri uri, int index, IContentObserver observer,
                               boolean notifyForDescendants, Object observersLock,
                               int uid, int pid, int userHandle) {
    // If this is the leaf node add the observer
    if (index == countUriSegments(uri)) {
        mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
                uid, pid, userHandle));
        return;
    }

    // Look to see if the proper child already exists
    String segment = getUriSegment(uri, index);
    int N = mChildren.size();
    for (int i = 0; i < N; i++) {
        ObserverNode node = mChildren.get(i);
        if (node.mName.equals(segment)) {
            node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
                    observersLock, uid, pid, userHandle);
            return;
        }
    }

    // No child found, create one
    ObserverNode node = new ObserverNode(segment);
    mChildren.add(node);
    node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
            observersLock, uid, pid, userHandle);
    }
  • 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/content/ContentService.java中。

首先把觀察者observer再次封裝到mObservers中,mObservers是ArrayList的例項,後面依次遍歷整個需要監聽的Uri,用segment生產一個ObserverNode例項,儲存在ArrayList的例項mChildren中。

觀察者都被封裝好以後,就等待資料的變化了。在上文ContentProvider的原理中,可知,ContentProvider的需求者拿到ContentProvider提供者的遠端呼叫後,兩個APP直接進行互動了,任何資料變化並沒有經過ContentService,因此,ContentService是如何監測ContentProvider資料發生變化並通知到觀察者呢?這裡的機制便是ContentProvider提供者的資料發生變化時,ContentProvider提供者必須主動通過notifyChange()方法通知ContentService資料發生了變化。下面以聯絡人的資料庫變化為例,闡述這個過程。

資料變化通知過程

新增一個聯絡人,呼叫ContactsProvider的insert()方法,如下:

public Uri insert(Uri uri, ContentValues values) {
    incrementStats(mInsertStats, mInsertInBatchStats);
    ContactsTransaction transaction = startTransaction(false);
    try {
        Uri result = insertInTransaction(uri, values);
        if (result != null) {
            transaction.markDirty();
        }
        transaction.markSuccessful(false);
        return result;
    } finally {
        endTransaction(false);
    }
}
  • 這個方法定義在檔案packages/providers/ContactsProvider/src/com/android/providers/contacts/AbstractContactsProvider.java中。

上面的程式碼中,一次插入的業務抽象成一個ContactsTransaction,然後呼叫insertInTransaction()方法把資料插入資料庫成功後,呼叫transaction.markDirty()標記當前的資料狀態有變化,然後呼叫endTransaction()結束當前的業務。如下:

private void endTransaction(boolean callerIsBatch) {
    ContactsTransaction transaction = mTransactionHolder.get();
    if (transaction != null && (!transaction.isBatch() || callerIsBatch)) {
        boolean notify = false;
        try {
            if (transaction.isDirty()) {
                notify = true;
            }
            transaction.finish(callerIsBatch);
            if (notify) {
                notifyChange();
            }
        } finally {
            // No matter what, make sure we clear out the thread-local transaction reference.
            mTransactionHolder.set(null);
        }
    }
}
  • 這個方法定義在檔案packages/providers/ContactsProvider/src/com/android/providers/contacts/AbstractContactsProvider.java中。

上面的程式碼,通過mTransactionHolder.get()獲取剛剛進行的業務transaction,如果的這個業務被標記為markDirty(),transaction.isDirty()返回true,所以notify被置成true,所以會呼叫notifyChange()。往下看這個方法:

protected void notifyChange(boolean syncToNetwork, boolean syncToMetadataNetwork) {
    getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
            syncToNetwork);

    getContext().getContentResolver().notifyChange(MetadataSync.METADATA_AUTHORITY_URI,
            null, syncToMetadataNetwork);
}
  • 這個方法定義在檔案packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java中。

上面的程式碼,獲取到ContentResolver()物件,和query()類似,然後呼叫notifyChange()通知ContentResolver資料發生了變化,如下:

public void notifyChange(@NonNull Uri uri, ContentObserver observer, boolean syncToNetwork,
        @UserIdInt int userHandle) {
    try {
        getContentService().notifyChange(
                uri, observer == null ? null : observer.getContentObserver(),
                observer != null && observer.deliverSelfNotifications(),
                syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
                userHandle);
    } catch (RemoteException e) {
    }
}
  • 這個方法定義在檔案frameworks/base/core/java/android/content/ContentResolver.java中。

上面的程式碼呼叫getContentService()方法,從上文可知,getContentService()獲取到的是ContentService的遠端呼叫,因此getContentService().notifyChange()實質會執行到ContentService的notifyChange()方法,如下:

public void notifyChange(Uri uri, ......) {
    ......
    try {
        ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
        synchronized (mRootNode) {
            mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
                    flags, userHandle, calls);
        }
        final int numCalls = calls.size();
        for (int i=0; i<numCalls; i++) {
            ObserverCall oc = calls.get(i);
            try {
                oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
            } catch (RemoteException ex) {
                ......
            }
        }
        ......
        }
    } finally {
        restoreCallingIdentity(identityToken);
    }
}
  • 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/content/ContentService.java中。

上面的程式碼中的mRootNode變數,在註冊觀察者的章節中,mRootNode封裝了所有的觀察者。呼叫collectObserversLocked()根據觀察者監聽的Uri型別把觀察者封裝到ArrayList的例項calls中,然後呼叫oc.mObserver.onChange()方法通知APP資料發生變化。

總結

本文詳細闡述了ContentProvider的原理,從ContentProvider的query過程入手,全面詳細分析了從query()的發起APP到ActivityManagerService,再到ContentProvider提供程式的整個流程和互動過程。以及分析了ContentService在ContentProvider的資料變化監測通知的架構中,扮演了觀察者模式中的抽象主題,具體主題部分。