ContentProvider是Android四大組件之一,承擔着跨進程數據訪問的重要職責。本文就從一次ContentProvider訪問入手,分析下它是怎么完成跨進程數據訪問的。
既然是跨進程,那就必須有一個客戶端進程和一個ContentProvider進程,我們先從客戶端進程分析,看它如何訪問ContentProvider進程。以Query操作為例,一般情況下,當我們需要訪問ContentProvider的時候一般都會執行這么一句:
getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
ContentResolver是什么?Google的解釋是“This class provides applications access to the content model”。實際上我們正是通過ContentResolver來獲取一個IContentProvider對象,通過IContentProvider對象我們盡可以進行IPC通訊了。getContentResolver()方法定義Context類中,實際上Context是一個抽象類,在客戶端應用程序中getContext()實際上返回的是一個ContextImp對象,getContentResolver()方法就定義在ContextImp.java中,並且最終返回ContextImp的內部類ApplicationContentResolver,從名字上看這是一個Application級別的對象。
ApplicationContentResolver我們稍后再說,先看下query方法都干了什么:
public final Cursor query(final Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null) { return null; } IContentProvider stableProvider = null; try { long startTime = SystemClock.uptimeMillis(); ICancellationSignal remoteCancellationSignal = null; if (cancellationSignal != null) { cancellationSignal.throwIfCanceled(); remoteCancellationSignal = unstableProvider.createCancellationSignal(); cancellationSignal.setRemote(remoteCancellationSignal); } Cursor qCursor; try { qCursor = unstableProvider.query(uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); } catch (DeadObjectException e) { // The remote process has died... but we only hold an unstable // reference though, so we might recover!!! Let's try!!!! // This is exciting!!1!!1!!!!1 unstableProviderDied(unstableProvider); stableProvider = acquireProvider(uri); if (stableProvider == null) { return null; } qCursor = stableProvider.query(uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); } if (qCursor == null) { return null; } // force query execution qCursor.getCount(); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder); // Wrap the cursor object into CursorWrapperInner object CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, stableProvider != null ? stableProvider : acquireProvider(uri)); stableProvider = null; return wrapper; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. return null; } finally { if (unstableProvider != null) { releaseUnstableProvider(unstableProvider); } if (stableProvider != null) { releaseProvider(stableProvider); } } }
上面的代碼有四個關鍵步驟:
1. acquireUnstableProvider
2. unstableProvider.query(......)
3. qCursor.getCount();
4. return new CursorWrapperInner(......)
接下來我們一步一步分析這四個關鍵步驟。
第一步:acquireUnstableProvider
乍看上去感覺怪怪的,好端端的為什么加上了一個Unstable的標簽?難道還有stable的不成?事實確實如此,我們知道此時的ContentResolver實際上是一個ApplicationContentResovler對象,來看下ApplicationContentResovler
private static final class ApplicationContentResolver extends ContentResolver { private final ActivityThread mMainThread; private final UserHandle mUser; public ApplicationContentResolver( Context context, ActivityThread mainThread, UserHandle user) { super(context); mMainThread = Preconditions.checkNotNull(mainThread); mUser = Preconditions.checkNotNull(user); } @Override protected IContentProvider acquireProvider(Context context, String auth) { return mMainThread.acquireProvider(context, auth, mUser.getIdentifier(), true); } @Override protected IContentProvider acquireExistingProvider(Context context, String auth) { return mMainThread.acquireExistingProvider(context, auth, mUser.getIdentifier(), true); } @Override public boolean releaseProvider(IContentProvider provider) { return mMainThread.releaseProvider(provider, true); } @Override protected IContentProvider acquireUnstableProvider(Context c, String auth) { return mMainThread.acquireProvider(c, auth, mUser.getIdentifier(), false); } @Override public boolean releaseUnstableProvider(IContentProvider icp) { return mMainThread.releaseProvider(icp, false); } @Override public void unstableProviderDied(IContentProvider icp) { mMainThread.handleUnstableProviderDied(icp.asBinder(), true); } }
實際上,是否是stable的,都將調用ActivityThread的acquireProvider方法,區別就是最后的一個參數boolean stable。這個機制是API 16引入的,文章的最后會對此進行說明。現在我們只要知道它最終走到了ActivityThread的acquireProvider就可以了。在ActivityThread的acquireProvider方法中,我們首先會去acquireExistingProvider,從字面上就可以看出這是從一個類緩存的地方讀取已經保存的ContentProvider對象,如果不存在,就會調用ActivityManagerNative.getDefault().getContentProvider(getApplicationThread(), auth, userId, stable);從這兒開始,ActivityManagerService就要登場了,上面的代碼最終會通過binder通信調用ActivityManagerService的getContentProviderImpl,這塊的邏輯比較復雜,涉及到了Android組件啟動的過程,我們只需知道客戶端調用會阻塞在ActivityManagerNative.getDefault().getContentProvider,ActivityManagerService啟動目標ContentProvider進程后(如果ContentProvider進程已經存在則不必重啟),返回一個目標ContentProvider的實例。在這兒需要說明的是,ContentProvider可以再Manifest中配置一個叫做android:multiprocess的屬性,默認值是false,表示ContentProvider是單例的,無論哪個客戶端應用的訪問都將是一個ContentProvider對象(當然,必須是同一個ContentProvider,即Uri或者Authority name是一個),如果設為true,系統會為每一個訪問該ContentProvider的進程創建一個實例。因為android:multiprocess的默認值是false,所以我們在寫自己的ContentProvider的時候還是要注意並發的情況。
扯得有點遠了,回到我們之前步驟,我們已經得到了ContentProvider的實例,這個時候第一步就完成了,接下來看第二步。
第二步:unstableProvider.query(......)
unstableProvider實際上是IContentProvider實例,IContentProvider是進行IPC通訊的接口,這個query實際上調用的是目標ContentProvider中的query方法,當然,在真正調用目標ContentProvider的query方法之前,還需要經過enforceReadPermission方法,這一步主要是看下該ContentProvier有沒有export,讀寫權限等等(enforceReadPermission方法只判斷讀權限)。隨后執行query方法,並且返回一個cursor對象。
以上就是第二步的大致邏輯,不過不要以為這么簡單就結束了。IContentProvider的query可是跨進程的,我們知道ContentProvider的query方法可是五花八門,有訪問數據庫返回SQLiteCursor的,有返回MatrixCursor的等等,那么IContentProvider返回的那個cursor到底是什么呢?我們來看下IContentProvider的這個IPC通信到底是怎么回事
IPC通信需要兩端,對於我們的例子,這兩段分別是ContentProviderProxy和ContentProviderNative,首先會執行ContentProviderProxy的query方法,然后通過binder通信執行ContentProviderNative的onTransact方法。ContentProviderProxy的query方法有一下五個主要步驟:
1. new一個BulkCursorToCursorAdaptor對象——adaptor
2. 填充data用於binder通信
3. 調用mRemote.transact,這是一個阻塞的過程,直到ContentProviderNative的onTransact方法返回
4. 讀取reply數據,new一個BulkCursorDescriptor並以此初始化adaptor
5. return adaptor
ContentProviderNative的onTransact會調用ContentProvider的query方法,並根據query返回的cursor初始化一個CursorToBulkCursorAdaptor對象,最終將BulkCursorDescriptor對象寫入reply中。
至此我們知道了,不管我們的ContentProvider query方法返回的到底是什么樣的cursor,最終在客戶端進程都將會被封裝在一個BulkCursorToCursorAdaptor對象中,那么這個BulkCursorToCursorAdaptor對象是不是就是我們在客戶端調用query返回的最終類型呢?別急,往下看。
第三步:qCursor.getCount();
這一步看似雞肋,實際上涉及到了SQLiteCursor的一個設計要點,那就是SQLiteCursor的內存共享。getCount會調用SQLiteCursor的fillWindow,在以后的文章中我會在講到SQLiteCursor,在此我們只要知道它是強制執行數據庫query就可以了。
第四步:return new CursorWrapperInner(......)
哈,看到了吧,我們在客戶端調用query最終返回的是一個CursorWrapperInner類型,它是ContentResolver的一個內部類。實際上我們常用的getCount,onMove等一些列方法都是通過BulkCursorToCursorAdaptor和CursorToBulkCursorAdaptor的交互實現的。
到此,ContentProvider的訪問流程就結束了,下面說一下開頭買的坑:unstable和stable的ContentProvider。
在4.1之前,我們都可能會遇到過這樣的場景,我們的應用程序訪問了ContentProvider,但是這個ContentProvider意外掛了,這個時候我們的應用程序也將被連帶殺死!這是Android處於對數據安全的考慮而做的決定,不過貌似Google也感覺這樣的方式不太友好,所以在4.1以后提出了stable和unstable的概念。對於ContentResolver的query方法,我們將默認使用unstable的ContentProvider。看看下面的代碼
Cursor qCursor; try { qCursor = unstableProvider.query(uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); } catch (DeadObjectException e) { // The remote process has died... but we only hold an unstable // reference though, so we might recover!!! Let's try!!!! // This is exciting!!1!!1!!!!1 unstableProviderDied(unstableProvider); stableProvider = acquireProvider(uri); if (stableProvider == null) { return null; } qCursor = stableProvider.query(uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); }
上面是ContentResolver query中的一部分,可以看出unstable ContentProvider在query過程中如果發生了DeadObjectExeption則會被捕獲,進而重新獲取一個stable的ContentProvider。其實深入分析stable和unstable的ContentProvider還會有很多內容,以后有時間再說。