Android Cursor 源碼分析

1. 本文目的

Android ContentProvider提供了進程間數據交換的一種機制。而數據庫的查詢就是這種機制的應用。那麼app通過Uri查詢數據庫而得到的Cursor究竟是個什麼東西?爲何可以爲我們提供另一個進程的數據?本文以getContentResolver().query(……)函數爲起點,全面分析Cursor家族關係類圖,理清Cursor跨進程通信的機制。

1.1 客戶端的Cursor對象

假設B進程中有一個ContentProvider,A進程通過Uri查詢這個ContentProvider,從而得到一個Cursor。可能的代碼如下:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. ContentResolver cr = mContext.getContentResolver();//mContext是一個Context對象  
  2. Cursor cs = cr.query(uri,null,null,null,null);  

爲了知道上述代碼得到的Cursor的真實面貌,我們需要看下上述query的調用途徑。query函數的實現如下:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public final Cursor query(final Uri uri, String[] projection,  
  2.         String selection, String[] selectionArgs, String sortOrder,  
  3.         CancellationSignal cancellationSignal) {  
  4.     IContentProvider unstableProvider = acquireUnstableProvider(uri);  
  5.     //省略無關代碼  
  6.     try {  
  7.         //省略無關代碼  
  8.         Cursor qCursor;  
  9.         try {  
  10.             qCursor = unstableProvider.query(uri, projection,  
  11.                     selection, selectionArgs, sortOrder, remoteCancellationSignal);  
  12.         } catch (DeadObjectException e) {  
  13.           //省略無關代碼  
  14.         }  
  15.         if (qCursor == null) {  
  16.             return null;  
  17.         }  
  18.        //省略無關代碼  
  19.         CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,  
  20.                 stableProvider != null ? stableProvider : acquireProvider(uri));  
  21.         stableProvider = null;  
  22.         return wrapper;  
  23.     } catch (RemoteException e) {  
  24.        //省略無關代碼  
  25.     } finally {  
  26.       //省略無關代碼  
  27.     }  




















爲了方便分析,省略了無關代碼。從以上代碼可以看出:

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實現:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public Cursor query(Uri url, String[] projection, String selection,  
  2.         String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)  
  3.                 throws RemoteException {  
  4.     BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();  
  5.     Parcel data = Parcel.obtain();  
  6.     Parcel reply = Parcel.obtain();  
  7.     try {  
  8.         data.writeInterfaceToken(IContentProvider.descriptor);  
  9.   
  10.         url.writeToParcel(data, 0);  
  11.         int length = 0;  
  12.         if (projection != null) {  
  13.             length = projection.length;  
  14.         }  
  15.         data.writeInt(length);  
  16.         for (int i = 0; i < length; i++) {  
  17.             data.writeString(projection[i]);  
  18.         }  
  19.         data.writeString(selection);  
  20.         if (selectionArgs != null) {  
  21.             length = selectionArgs.length;  
  22.         } else {  
  23.             length = 0;  
  24.         }  
  25.         data.writeInt(length);  
  26.         for (int i = 0; i < length; i++) {  
  27.             data.writeString(selectionArgs[i]);  
  28.         }  
  29.         data.writeString(sortOrder);  
  30.         data.writeStrongBinder(adaptor.getObserver().asBinder());  
  31.         data.writeStrongBinder(cancellationSignal != null ? cancellationSignal.asBinder() : null);  
  32.   
  33.         mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);  
  34.   
  35.         DatabaseUtils.readExceptionFromParcel(reply);  
  36.   
  37.         if (reply.readInt() != 0) {  
  38.             BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);  
  39.             adaptor.initialize(d);  
  40.         } else {  
  41.             adaptor.close();  
  42.             adaptor = null;  
  43.         }  
  44.        return adaptor;  
  45.     } catch (RemoteException ex) {  
  46.         adaptor.close();  
  47.         throw ex;  
  48.     } catch (RuntimeException ex) {  
  49.         adaptor.close();  
  50.         throw ex;  
  51.     } finally {  
  52.         data.recycle();  
  53.         reply.recycle();  
  54.     }  
  55. }  
從上面代碼可以得之,query函數返回的Cursor的真實對象是BulkCursorToCursorAdaptor。在query函數內部通過transact函數把query請求傳遞到ContentProviderNative端。transact執行完畢後會返回一個Parcel reply。從reply中構造一個BulkCursorDescriptor。然後由這個BulkCursorDescriptor初始化BulkCursorToCursorAdaptor。BulkCursorToCursorAdaptor中一個相當重要的賦值操作如下:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public void initialize(BulkCursorDescriptor d) {  
  2.     mBulkCursor = d.cursor;  
  3.     mColumns = d.columnNames;  
  4.     mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns);  
  5.     mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls;  
  6.     mCount = d.count;  
  7.     if (d.window != null) {  
  8.         setWindow(d.window);  
  9.     }  
  10. }  

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的實現。

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1.     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)  
  2.             throws RemoteException {  
  3.         try {  
  4.             switch (code) {  
  5.                 case QUERY_TRANSACTION:  
  6.                 {  
  7.                     data.enforceInterface(IContentProvider.descriptor);  
  8.   
  9.                     Uri url = Uri.CREATOR.createFromParcel(data);  
  10.   
  11.                     // String[] projection  
  12.                     int num = data.readInt();  
  13.                     String[] projection = null;  
  14.                     if (num > 0) {  
  15.                         projection = new String[num];  
  16.                         for (int i = 0; i < num; i++) {  
  17.                             projection[i] = data.readString();  
  18.                         }  
  19.                     }  
  20.   
  21.                     // String selection, String[] selectionArgs...  
  22.                     String selection = data.readString();  
  23.                     num = data.readInt();  
  24.                     String[] selectionArgs = null;  
  25.                     if (num > 0) {  
  26.                         selectionArgs = new String[num];  
  27.                         for (int i = 0; i < num; i++) {  
  28.                             selectionArgs[i] = data.readString();  
  29.                         }  
  30.                     }  
  31.   
  32.                     String sortOrder = data.readString();  
  33.                     IContentObserver observer = IContentObserver.Stub.asInterface(  
  34.                             data.readStrongBinder());  
  35.                     ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(  
  36.                             data.readStrongBinder());  
  37.   
  38.                     Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder,  
  39.                             cancellationSignal);  
  40.                     if (cursor != null) {  
  41.                         CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(  
  42.                                 cursor, observer, getProviderName());  
  43.                         BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();  
  44.   
  45.                         reply.writeNoException();  
  46.                         reply.writeInt(1);  
  47.                         d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);  
  48.                     } else {  
  49.                         reply.writeNoException();  
  50.                         reply.writeInt(0);  
  51.                     }  
  52.   
  53.                     return true;  
  54.                 }  
  55.             }  
  56. …………  

上面的代碼是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文件:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public Cursor rawQueryWithFactory(  
  2.         CursorFactory cursorFactory, String sql, String[] selectionArgs,  
  3.         String editTable, CancellationSignal cancellationSignal) {  
  4.     acquireReference();  
  5.     try {  
  6.         SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,  
  7.                 cancellationSignal);  
  8.         return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,  
  9.                 selectionArgs);  
  10.     } finally {  
  11.         releaseReference();  
  12.     }  
  13. }  
由上面的代碼可以得之,Cursor來自於SQLiteDirectCursorDriver的query。那不妨在看看其query的實現:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public Cursor query(CursorFactory factory, String[] selectionArgs) {  
  2.     final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal);  
  3.     final Cursor cursor;  
  4.     try {  
  5.         query.bindAllArgsAsStrings(selectionArgs);  
  6.   
  7.         if (factory == null) {  
  8.             cursor = new SQLiteCursor(this, mEditTable, query);  
  9.         } else {  
  10.             cursor = factory.newCursor(mDatabase, this, mEditTable, query);  
  11.         }  
  12.     } catch (RuntimeException ex) {  
  13.         query.close();  
  14.         throw ex;  
  15.     }  
  16.   
  17.     mQuery = query;  
  18.     return cursor;  
  19. }  

這裏我們假設factory爲空,那麼此處的Cursor終於露出了它的真實面貌:SQLiteCursor。 一路跟蹤的真是辛苦,水落石出了!

通過下面的類圖我們整理下我們的思路:


從圖中可以看出BulkCursorToCursorAdaptor的成員變量的mBulkCursor是一個IBuilCursor接口,它的真實對象其實是一個BulkCursorProxy。BulkCursorProxy是一個代理端,也是一個Bp端,通過Binder通信把函數調用請求轉發給另一個進程中的Bn端BulkCursorNative。圖中,綠線方框部分運行在app進程中,紅線方框部分運行在ContentProvider進程中。

CursorToBulkCursorAdaptor中的mCursor的真實對象也揭曉了:SQLiteCursor。從名字上看也知道這個對象和SQLite有關係了。它內部有一個SQLiteQuery,負責數據庫的查詢和CursorWindow的建立。紅線和綠線方框的交叉處CursorWindow是共享內存的抽象。在兩個進程中都存在一份映射。

自此,Cursor的分析全部結束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章