现在的位置: 首页 > 综合 > 正文

Android Cursor 源码分析

2017年09月20日 ⁄ 综合 ⁄ 共 10555字 ⁄ 字号 评论关闭

1. 本文目的

Android ContentProvider提供了进程间数据交换的一种机制。而数据库的查询就是这种机制的应用。那么app通过Uri查询数据库而得到的Cursor究竟是个什么东西?为何可以为我们提供另一个进程的数据?本文以getContentResolver().query(……)函数为起点,全面分析Cursor家族关系类图,理清Cursor跨进程通信的机制。

1.1 客户端的Cursor对象

假设B进程中有一个ContentProvider,A进程通过Uri查询这个ContentProvider,从而得到一个Cursor。可能的代码如下:

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. ContentResolver cr = mContext.getContentResolver();//mContext是一个Context对象  
  2. Cursor cs = cr.query(uri,null,null,null,null);  


为了知道上述代码得到的Cursor的真实面貌,我们需要看下上述query的调用途径。query函数的实现如下:

[java] view
plain
copy在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
plain
copy在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
plain
copy在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
plain
copy在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
plain
copy在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
plain
copy在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的分析全部结束。

抱歉!评论已关闭.