1. 本文目的
Android ContentProvider提供了進程間數據交換的一種機制。而數據庫的查詢就是這種機制的應用。那麼app通過Uri查詢數據庫而得到的Cursor究竟是個什麼東西?爲何可以爲我們提供另一個進程的數據?本文以getContentResolver().query(……)函數爲起點,全面分析Cursor家族關係類圖,理清Cursor跨進程通信的機制。
1.1 客戶端的Cursor對象
假設B進程中有一個ContentProvider,A進程通過Uri查詢這個ContentProvider,從而得到一個Cursor。可能的代碼如下:
- ContentResolver cr = mContext.getContentResolver();//mContext是一個Context對象
- Cursor cs = cr.query(uri,null,null,null,null);
爲了知道上述代碼得到的Cursor的真實面貌,我們需要看下上述query的調用途徑。query函數的實現如下:
- public final Cursor query(final Uri uri, String[] projection,
- String selection, String[] selectionArgs, String sortOrder,
- CancellationSignal cancellationSignal) {
- IContentProvider unstableProvider = acquireUnstableProvider(uri);
- //省略無關代碼
- try {
- //省略無關代碼
- Cursor qCursor;
- try {
- qCursor = unstableProvider.query(uri, projection,
- selection, selectionArgs, sortOrder, remoteCancellationSignal);
- } catch (DeadObjectException e) {
- //省略無關代碼
- }
- if (qCursor == null) {
- return null;
- }
- //省略無關代碼
- CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
- stableProvider != null ? stableProvider : acquireProvider(uri));
- stableProvider = null;
- return wrapper;
- } catch (RemoteException e) {
- //省略無關代碼
- } finally {
- //省略無關代碼
- }
爲了方便分析,省略了無關代碼。從以上代碼可以看出:
1.通過query函數返回的Cursor是一個CursorWrapperInner對象;
2.CursorWrapperInner是一個包裝類,是通過以IContentProvider 的query函數返回的Cursor對象構建的;
那麼有兩個疑問:
1.IContentProvider 的query函數返回的Cursor,真實對象是?
2.CursorWrapperInner類起什麼作用,爲什麼要把IContentProvider 的query函數返回的Cursor作爲參數,從新構造一個CursorWrapperInner?
首先來看下類圖:
從上述類圖可以得知:
CursorWrapperInner是ContentResolver的內部類,繼承自CrossProcessCursorWrapper。CrossProcessCursorWrapper從名字上看是一個實現了跨進程通信的Cursor包裝類。從類圖上也驗證了這一點,CrossProcessCursorWrapper繼承自CursorWrapper,實現了CrossProcessCursor接口。而CursorWrapper是一個包裝類,它實現了Cursor接口的所有功能,它內部含有一個mCursor成員變量指向一個Cursor接口,從而可以得知,這是一個代理模式。CursorWrapper委託它內部的mCursor對象來實現所有的Cursor功能接口。CrossProcessCursor繼承自Cursor,它主要是用於跨進程通信。
綜上,目前我們可以知道,CrossProcesCursorWrapper實現了所有的Cursor接口,但是這些接口功能的完成全部委託它父類內部的mCursor來完成。那麼mCursor的真實對象是什麼呢?暫且推測,mCursor應該是一個實現了CrossProcessCursor的對象。
總結:客戶端拿到的Cursor的真實對象是:CursorWarpprtInner類。
1.2 CursorWrapper內部的Cursor真實面目
從上節我們已經知道,通過代理模式,CursorWrapperInner最終會委託CursorWrapper來完成實際功能。現在就看看CursorWrapper內部的mCursor的真實面目。mCursor來自於IContentProvider 的query函數所返回的Cursor對象。那麼這個Cursor對象是啥呢?那就要看看IContentProvider的query函數的實現了。IContentProvider的實際上是一個ContentProviderProxy對象。它是一個代理類,也是一個Binder的Bp端,將函數調用的參數打包發送給ContentProviderNative,最終由ContentProviderNative把來調用ContentProvider的具體函數。
ContentProviderProxy,ContentProviderNative,ContentProvider,IContentProvider的關係如下:
下面是ContentProviderProxy的query實現:
- public Cursor query(Uri url, String[] projection, String selection,
- String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
- throws RemoteException {
- BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- try {
- data.writeInterfaceToken(IContentProvider.descriptor);
- url.writeToParcel(data, 0);
- int length = 0;
- if (projection != null) {
- length = projection.length;
- }
- data.writeInt(length);
- for (int i = 0; i < length; i++) {
- data.writeString(projection[i]);
- }
- data.writeString(selection);
- if (selectionArgs != null) {
- length = selectionArgs.length;
- } else {
- length = 0;
- }
- data.writeInt(length);
- for (int i = 0; i < length; i++) {
- data.writeString(selectionArgs[i]);
- }
- data.writeString(sortOrder);
- data.writeStrongBinder(adaptor.getObserver().asBinder());
- data.writeStrongBinder(cancellationSignal != null ? cancellationSignal.asBinder() : null);
- mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
- DatabaseUtils.readExceptionFromParcel(reply);
- if (reply.readInt() != 0) {
- BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);
- adaptor.initialize(d);
- } else {
- adaptor.close();
- adaptor = null;
- }
- return adaptor;
- } catch (RemoteException ex) {
- adaptor.close();
- throw ex;
- } catch (RuntimeException ex) {
- adaptor.close();
- throw ex;
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
- public void initialize(BulkCursorDescriptor d) {
- mBulkCursor = d.cursor;
- mColumns = d.columnNames;
- mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns);
- mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls;
- mCount = d.count;
- if (d.window != null) {
- setWindow(d.window);
- }
- }
d.window是一個CursorWindow對象。這個對象實際上代表着一塊共享內存,存儲着查詢後的結果集。詳情請參見:http://blog.csdn.net/ifloveelse/article/details/28394103。而mBulkCursor是一個IBulkCursor接口。這個接口起到數據傳輸的作用。分析到這一步,Cursor的”全家幅“更加的詳細了:
總結:在本小節開頭就提出的問題:CursorWrapper內部的Cursor真實面目是誰?現在也可以解答了:BulkCursorToCursorAdaptor。從名字上看這是個適配器,將Cursor的功能接口通過轉換,調用IBulkCursor來實現。自此,上面的這個類圖就是APP端進程中Cursor的全部關係了。那麼IBulkCursor又是做什麼的呢?下一小節講解。
1.3 IBulkCursor家族
BulkCursorToCursorAdaptor的mBulkCursor來自於ContentProviderNative的返回值。爲了弄清楚IBulkCursor的真實面貌,還要去看看ContentProviderNative的實現。
- public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
- throws RemoteException {
- try {
- switch (code) {
- case QUERY_TRANSACTION:
- {
- data.enforceInterface(IContentProvider.descriptor);
- Uri url = Uri.CREATOR.createFromParcel(data);
- // String[] projection
- int num = data.readInt();
- String[] projection = null;
- if (num > 0) {
- projection = new String[num];
- for (int i = 0; i < num; i++) {
- projection[i] = data.readString();
- }
- }
- // String selection, String[] selectionArgs...
- String selection = data.readString();
- num = data.readInt();
- String[] selectionArgs = null;
- if (num > 0) {
- selectionArgs = new String[num];
- for (int i = 0; i < num; i++) {
- selectionArgs[i] = data.readString();
- }
- }
- String sortOrder = data.readString();
- IContentObserver observer = IContentObserver.Stub.asInterface(
- data.readStrongBinder());
- ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
- data.readStrongBinder());
- Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder,
- cancellationSignal);
- if (cursor != null) {
- CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
- cursor, observer, getProviderName());
- BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
- reply.writeNoException();
- reply.writeInt(1);
- d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
- } else {
- reply.writeNoException();
- reply.writeInt(0);
- }
- return true;
- }
- }
- …………
上面的代碼是ContentProviderNative中onTransact函數對query的處理。在這部分代碼中,又冒出了個CursorToBulkCursorAdaptor對象。這個對象的構造函數是一個Cursor對象。那麼這個Cursor對象的又是誰呢?邏輯越來越複雜了。Cursor是由query函數返回。由1.2節中所提供的ContentProvider的類圖可以得之,這個query調用的是ContentProvider的query函數。那麼ContentProvider的query函數中的Cursor又是哪裏來的呢?這裏直接給出答案:SQLiteDatabase。ContentProvider是一個抽象類,我們需要自己實現其中的query函數。一般,在query中,我們通過SQLiteDatabase查詢自定義的數據庫,得到一個Cursor對象。這個過程就省略。我們需要知道的是:SQLiteDatabase的query函數返回一個Cursor。這個Cursor用來構建了一個CursorToBulkCursorAdaptor對象。
下面就看看SQLiteDatabase返回的Cursor的真實面貌。下面是SQLiteDatabase中query的最終調用函數。具體的代碼可以參考SQLiteDatabase.java文件:
- public Cursor rawQueryWithFactory(
- CursorFactory cursorFactory, String sql, String[] selectionArgs,
- String editTable, CancellationSignal cancellationSignal) {
- acquireReference();
- try {
- SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
- cancellationSignal);
- return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
- selectionArgs);
- } finally {
- releaseReference();
- }
- }
- public Cursor query(CursorFactory factory, String[] selectionArgs) {
- final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal);
- final Cursor cursor;
- try {
- query.bindAllArgsAsStrings(selectionArgs);
- if (factory == null) {
- cursor = new SQLiteCursor(this, mEditTable, query);
- } else {
- cursor = factory.newCursor(mDatabase, this, mEditTable, query);
- }
- } catch (RuntimeException ex) {
- query.close();
- throw ex;
- }
- mQuery = query;
- return cursor;
- }
這裏我們假設factory爲空,那麼此處的Cursor終於露出了它的真實面貌:SQLiteCursor。 一路跟蹤的真是辛苦,水落石出了!
通過下面的類圖我們整理下我們的思路:
從圖中可以看出BulkCursorToCursorAdaptor的成員變量的mBulkCursor是一個IBuilCursor接口,它的真實對象其實是一個BulkCursorProxy。BulkCursorProxy是一個代理端,也是一個Bp端,通過Binder通信把函數調用請求轉發給另一個進程中的Bn端BulkCursorNative。圖中,綠線方框部分運行在app進程中,紅線方框部分運行在ContentProvider進程中。
CursorToBulkCursorAdaptor中的mCursor的真實對象也揭曉了:SQLiteCursor。從名字上看也知道這個對象和SQLite有關係了。它內部有一個SQLiteQuery,負責數據庫的查詢和CursorWindow的建立。紅線和綠線方框的交叉處CursorWindow是共享內存的抽象。在兩個進程中都存在一份映射。
自此,Cursor的分析全部結束。