原文地址在博客圓肴裙,已經(jīng)不用了跑筝,遷移過來。
ContentProvider是Android四大組件之一推励,承擔著跨進程數(shù)據(jù)訪問的重要職責。本文就從一次ContentProvider訪問入手肉迫,分析下它是怎么完成跨進程數(shù)據(jù)訪問的验辞。
既然是跨進程,那就必須有一個客戶端進程和一個ContentProvider進程喊衫,我們先從客戶端進程分析跌造,看它如何訪問ContentProvider進程。以Query操作為例族购,一般情況下壳贪,當我們需要訪問ContentProvider的時候一般都會執(zhí)行這么一句:
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的內(nèi)部類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);
}
}
}
上面的代碼有四個關(guān)鍵步驟:
1.acquireUnstableProvider
2.unstableProvider.query(......)
3.qCursor.getCount();
4.return new CursorWrapperInner(......)
接下來我們一步一步分析這四個關(guān)鍵步驟排吴。
第一步: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的街氢,都將調(diào)用ActivityThread的acquireProvider方法,區(qū)別就是最后的一個參數(shù)boolean stable睦袖。這個機制是API 16引入的珊肃,文章的最后會對此進行說明。現(xiàn)在我們只要知道它最終走到了ActivityThread的acquireProvider就可以了馅笙。在ActivityThread的acquireProvider方法中伦乔,我們首先會去acquireExistingProvider,從字面上就可以看出這是從一個類緩存的地方讀取已經(jīng)保存的ContentProvider對象董习,如果不存在烈和,就會調(diào)用ActivityManagerNative.getDefault().getContentProvider(getApplicationThread(), auth, userId, stable);
從這兒開始,ActivityManagerService就要登場了皿淋,上面的代碼最終會通過binder通信調(diào)用ActivityManagerService的getContentProviderImpl招刹,這塊的邏輯比較復雜,涉及到了Android組件啟動的過程窝趣,我們只需知道客戶端調(diào)用會阻塞在ActivityManagerNative.getDefault().getContentProvider
疯暑,ActivityManagerService啟動目標ContentProvider進程后(如果ContentProvider進程已經(jīng)存在則不必重啟),返回一個目標ContentProvider的實例高帖。在這兒需要說明的是缰儿,ContentProvider可以再Manifest中配置一個叫做android:multiprocess的屬性,默認值是false散址,表示ContentProvider是單例的乖阵,無論哪個客戶端應用的訪問都將是一個ContentProvider對象(當然宣赔,必須是同一個ContentProvider,即Uri或者Authority name是一個)瞪浸,如果設(shè)為true儒将,系統(tǒng)會為每一個訪問該ContentProvider的進程創(chuàng)建一個實例。因為android:multiprocess的默認值是false对蒲,所以我們在寫自己的ContentProvider的時候還是要注意并發(fā)的情況钩蚊。
扯得有點遠了,回到我們之前步驟蹈矮,我們已經(jīng)得到了ContentProvider的實例砰逻,這個時候第一步就完成了,接下來看第二步泛鸟。
第二步:unstableProvider.query(......)
unstableProvider實際上是IContentProvider實例蝠咆,IContentProvider是進行IPC通訊的接口,這個query實際上調(diào)用的是目標ContentProvider中的query方法北滥,當然刚操,在真正調(diào)用目標ContentProvider的query方法之前,還需要經(jīng)過enforceReadPermission方法再芋,這一步主要是看下該ContentProvier有沒有export菊霜,讀寫權(quán)限等等(enforceReadPermission方法只判斷讀權(quán)限)。隨后執(zhí)行query方法济赎,并且返回一個cursor對象鉴逞。
以上就是第二步的大致邏輯,不過不要以為這么簡單就結(jié)束了联喘。IContentProvider的query可是跨進程的华蜒,我們知道ContentProvider的query方法可是五花八門,有訪問數(shù)據(jù)庫返回SQLiteCursor的豁遭,有返回MatrixCursor的等等叭喜,那么IContentProvider返回的那個cursor到底是什么呢?我們來看下IContentProvider的這個IPC通信到底是怎么回事
IPC通信需要兩端蓖谢,對于我們的例子捂蕴,這兩端分別是ContentProviderProxy和ContentProviderNative,首先會執(zhí)行ContentProviderProxy的query
方法闪幽,然后通過binder通信執(zhí)行ContentProviderNative的onTransact
方法啥辨。ContentProviderProxy的query方法有一下五個主要步驟:
- new一個BulkCursorToCursorAdaptor對象——adaptor
- 填充data用于binder通信
- 調(diào)用mRemote.transact,這是一個阻塞的過程盯腌,直到ContentProviderNative的onTransact方法返回
- 讀取reply數(shù)據(jù)溉知,new一個BulkCursorDescriptor并以此初始化adaptor
- return adaptor
ContentProviderNative的onTransact會調(diào)用ContentProvider的query方法,并根據(jù)query返回的cursor初始化一個CursorToBulkCursorAdaptor對象,最終將BulkCursorDescriptor對象寫入reply中级乍。
至此我們知道了舌劳,不管我們的ContentProvider query方法返回的到底是什么樣的cursor,最終在客戶端進程都將會被封裝在一個BulkCursorToCursorAdaptor對象中玫荣,那么這個BulkCursorToCursorAdaptor對象是不是就是我們在客戶端調(diào)用query返回的最終類型呢甚淡?別急,往下看捅厂。
第三步:qCursor.getCount();
這一步看似雞肋贯卦,實際上涉及到了SQLiteCursor的一個設(shè)計要點,那就是SQLiteCursor的內(nèi)存共享焙贷。getCount會調(diào)用SQLiteCursor的fillWindow撵割,在以后的文章中我會在講到SQLiteCursor,在此我們只要知道它是強制執(zhí)行數(shù)據(jù)庫query就可以了辙芍。
第四步:return new CursorWrapperInner(......)
哈睁枕,看到了吧,我們在客戶端調(diào)用query最終返回的是一個CursorWrapperInner類型沸手,它是ContentResolver的一個內(nèi)部類。實際上我們常用的getCount注簿,onMove等一些列方法都是通過BulkCursorToCursorAdaptor和CursorToBulkCursorAdaptor的交互實現(xiàn)的契吉。
到此,ContentProvider的訪問流程就結(jié)束了诡渴,下面說一下開頭買的坑:unstable和stable的ContentProvider捐晶。
在4.1之前,我們都可能會遇到過這樣的場景妄辩,我們的應用程序訪問了ContentProvider惑灵,但是這個ContentProvider意外掛了,這個時候我們的應用程序也將被連帶殺死眼耀!這是Android處于對數(shù)據(jù)安全的考慮而做的決定英支,不過貌似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過程中如果發(fā)生了DeadObjectExeption則會被捕獲,進而重新獲取一個stable的ContentProvider鬼廓。其實深入分析stable和unstable的ContentProvider還會有很多內(nèi)容肿仑,以后有時間再說。