Android Cursor浅析
1. 本文目的
Android ContentProvider提供了进程间数据交换的一种机制。而数据库的查询就是这样的机制的应用。那么app通过Uri查询数据库而得到的Cursor到底是个什么东西?为何能够为我们提供还有一个进程的数据?本文以getContentResolver().query(……)函数为起点。全面分析Cursor家族关系类图,理清Cursor跨进程通信的机制。
1.1 client的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的对象。
总结:client拿到的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();}}
从上面代码能够得之,query函数返回的Cursor的真实对象是BulkCursorToCursorAdaptor。在query函数内部通过transact函数把query请求传递到ContentProviderNative端。
transact运行完成后会返回一个Parcel reply。从reply中构造一个BulkCursorDescriptor。然后由这个BulkCursorDescriptor初始化BulkCursorToCursorAdaptor。
BulkCursorToCursorAdaptor中一个相当重要的赋值操作例如以下:
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[] projectionint 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(); } }
由上面的代码能够得之,Cursor来自于SQLiteDirectCursorDriver的query。那最好还是在看看其query的实现:
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的分析所有结束。
Android Cursor浅析相关推荐
- Android Cursor自动更新的实现和原理
原文链接:http://www.sxrczx.com/pages/kohoh1992.github.io/cursor-auto-sync/index_1431878338570.html 在Andr ...
- Android Cursor类的概念和用法
http://www.2cto.com/kf/201109/103163.html 关于 Cursor 在你理解和使用 Android Cursor 的时候你必须先知道关于 Cursor 的几件事情: ...
- Android apps浅析01-Amazed:一个简单但令人上瘾的加速度为基础的大理石指导游戏。
Android apps浅析01-Amazed:一个简单但令人上瘾的加速度为基础的大理石指导游戏. 这个例子中只有4个类,一个绘制大理石类Marble,一个绘制迷宫类Maze,一个Amazed视图类, ...
- Android apps浅析01-Amazed:一个简单但令人上瘾的加速度为基础的大理石指导游戏。...
Android apps浅析01-Amazed:一个简单但令人上瘾的加速度为基础的大理石指导游戏. 这个例子中只有4个类,一个绘制大理石类Marble,一个绘制迷宫类Maze,一个Amazed视图类, ...
- Android Cursor的一些浅显见解
一.Cursor的数据模型 之前一直搞不懂Cursor的数据模型是怎么样的,等到看到下面别人博客中把Cursor比作.net中的DataReader才明白,原来是这么回事.但是没 学过.Net的同志可 ...
- [免费专栏] Android安全之Android加密算法浅析
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 转移发布平台通知:将不再在CSDN博客发布新文章,敬请移步知 ...
- Android(12)浅析 偏好设置 Preference(一)
Android(12)浅析 偏好设置 Preference(一) ### 官方基本用法:https://developer.android.google.cn/guide/topics/ui/sett ...
- android vold 挂载读写,【转】 android vold浅析(1)
[转] android vold浅析(1) (2011-11-29 17:41:58) 标签: android 杂谈 vold的全称是volume daemon.实际上是负责完成系统的CDROM, U ...
- android cursor 实例化,为什么Cursor放到带参数带返回值方法里就报错?
完整代码如下: 其中有大部分的代码被注释,那是可以成功运行的. 后来我想啊,游戏有好多种,但是需要统计的内容是一样的. 我就想传参 然后获得返回值的方式 来减少代码量啊. 但是把相关的查询代码放到 带 ...
- cursor 过滤 android,Android cursor query方法详解
1.cursor query 方法入参 public final Cursor query (Uri uri, String[] projection,String selection,String[ ...
最新文章
- 今年最新整理的《高频Java面试题集合》,聪明人已经收藏了!
- 直接输入地址访问服务器上的静态资源
- java用循环方式实现和计算机玩猜拳的程序
- 【树莓派学习笔记】八、两步安装VS Code (Visual Studio Code)
- 对象的序列化----将对象转化成二进制数据流
- linux设置自启动方式
- 《大道至简》 第一章 读后感
- 【迅雷VIP体验】免费获得迅雷会员,享受高速下载通道
- 孪生网络 Siamese Network
- 更改 matlab java 版本_64位 JDK 1.8 调用Matlab 2017b打包的jar
- 集成电路产业的芯片设计、芯片制造、封装测试
- c语言常量定义的数组初始化
- 教师继续教育 计算机知识,教师继续教育管理制度
- 我的团长我的团第九集
- BBC:关于睡眠你应该知道的十件事
- linux ps内存占用率,linux ps命令,查看某进程cpu和内存占用率情况, linux ps命令,查看进程cpu和内存占用率排序。 不指定...
- 瑞云渲染 | 全面支持Anima®4渲染插件,实现高精度的群集角色!
- ADN中国团队参加微软的Kinect全国大赛获三等奖,我们团队的创意项目用Kinect在Naviswork中虚拟漫游
- 王爽 汇编语言第二版 课程设计2
- 正则表达式、常用的方法、正则字符、定位符、转义字符、分组与反向、元素运动、元素左右运行、运动函数封装
热门文章
- js动态创建Form表单并提交
- fzu 2037 Maximum Value Problem
- [C#学习]在多线程中如何调用Winform[转]
- c#过滤字符串中相同的字符串只保留一个
- BerkeleyDB
- 泛型列表(List)的搜索和排序
- Oracle v$sql,v$sqlarea,v$sqltext区别
- 【Python 12】汇率兑换5.0(Lambda函数)
- putty+Xming使用方法
- 获取点击按钮的元素_怎么按顺序自动点击网页所有链接