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?
首先来看下类图:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaWZsb3ZlZWxzZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
从上述类图能够得知:
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的关系例如以下:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaWZsb3ZlZWxzZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
以下是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[] 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();
}
}
由上面的代码能够得之,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。 一路跟踪的真是辛苦,水落石出了!
通过以下的类图我们整理下我们的思路:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaWZsb3ZlZWxzZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
从图中能够看出BulkCursorToCursorAdaptor的成员变量的mBulkCursor是一个IBuilCursor接口,它的真实对象事实上是一个BulkCursorProxy。
BulkCursorProxy是一个代理端,也是一个Bp端,通过Binder通信把函数调用请求转发给还有一个进程中的Bn端BulkCursorNative。
图中。绿线方框部分执行在app进程中,红线方框部分执行在ContentProvider进程中。
CursorToBulkCursorAdaptor中的mCursor的真实对象也揭晓了:SQLiteCursor。
从名字上看也知道这个对象和SQLite有关系了。它内部有一个SQLiteQuery。负责数据库的查询和CursorWindow的建立。红线和绿线方框的交叉处CursorWindow是共享内存的抽象。在两个进程中都存在一份映射。
自此。Cursor的分析所有结束。
Android Cursor浅析的更多相关文章
- [Android Pro] 完美Android Cursor使用例子(Android数据库操作)
reference to : http://www.ablanxue.com/prone_10575_1.html 完美 Android Cursor使用例子(Android数据库操作),Androi ...
- Android Cursor类的概念和用法
http://www.2cto.com/kf/201109/103163.html 关于 Cursor 在你理解和使用 Android Cursor 的时候你必须先知道关于 Cursor 的几件事情: ...
- Android项目--浅析系统通讯录中的那些方法
系统通讯录,以前的版本虽然过时了,不过有些东西还是可以用. 1.开启系统联系人添加 /** 添加联系人 */ Intent intent = new Intent(Intent.ACTION_INSE ...
- 单独谈谈 Android Cursor 的使用细节
使用过 SQLite 数据库对 Cursor 应该不陌生,这里单独拿出来谈一下,加深对Android SQLite中使用 Cursor 的理解. 在你理解和使用 Android Cursor 的时候你 ...
- 如何检测 Android Cursor 泄漏
简介: 本文介绍如何在 Android 检测 Cursor 泄漏的原理以及使用方法,还指出几种常见的出错示例.有一些泄漏在代码中难以察觉,但程序长时间运行后必然会出现异常.同时该方法同样适合于其他需要 ...
- Android AIDL浅析及异步使用
AIDL:Android Interface Definition Language,即 Android 接口定义语言. AIDL 是什么 Android 系统中的进程之间不能共享内存,因此,需要提供 ...
- android framework浅析_转
Android系统从底向上一共分了4层,每一层都把底层实现封装,并暴露调用接口给上一层. 1. Linux内核(Linux Kernel) 1)Android运行在linux kernel 2.6之上 ...
- Android Cursor空指针的问题
最近几天无聊自己动手写个音乐播放器,用到Cursor来取得数据库中音乐文件的信息,但是当用到Cursor的时候总是报空指针错误,后来发现是模拟器上没有音乐文件,使用Cursor的时候 ,若Cursor ...
- Android框架浅析之锁屏(Keyguard)机制原理
最近终于成功的摆脱了FM收音机,迈向了新的模块:锁屏.状态栏.Launcher---姑且称之为“IDLE”小组,或许叫手机 美容小组,要是能施展下周星星同学的还我漂漂拳,岂不快哉. OK,闲话打住,咱 ...
随机推荐
- luogu3799 妖梦拼木棒
题目大意 有n根木棒,现在从中选4根,想要组成一个正三角形,问有几种选法?木棒长度都<=5000. 题解 根据容斥原理,三角形两条边分别由长度相等的单根木棒组成,另一条边由两条小于该边长的木棒构 ...
- 一个简单的演示用的Linux字符设备驱动程序
实现如下的功能:--字符设备驱动程序的结构及驱动程序需要实现的系统调用--可以使用cat命令或者自编的readtest命令读出"设备"里的内容--以8139网卡为例,演示了I/O端 ...
- Android4.0.4-在build.prop中添加属性的方法【转】
本文转载自:http://blog.csdn.net/imyfriend/article/details/8939964 1.在*.rc文件中用setprop添加,例如在源码android4.0\sy ...
- Swift - 将Data数据转换为[UInt8](bytes字节数组)
有时上传或者发送图片.文字时,需要将数据转换为 bytes 字节数组.下面介绍两种将 Data 转换为 [UInt8] 的方法. 假设我们有如下 Data 数据要转换: 1 let data = &q ...
- C Tricks(十七)—— 对角线元素的屏蔽、二维数组(矩阵)的遍历
1. 对角线元素的屏蔽 使用 if + continue 实现对对角线元素的屏蔽 for u in range(n): for v in range(n): if u == v: continue . ...
- JDK8新特性:函数式接口
一,定义 函数式接口,英文为Functional Interface.首先它是一个接口,那么它与其它接口有什么不同呢?不同点就是在这个接口中只允许有一个抽象方法. 这里的只允许一个抽象方法不包括以下几 ...
- javascript变量中基本类型和引用类型的详解解读
前言: Javascript语言中的变量和其他语言的变量有很大区别,javascript松散类型的本质,决定了它只是在特定时间时间保存特定值得名字而已.由于不存在定义某个变量必须保存何种数据类型值的规 ...
- GCC编译步骤
gcc -E t1.c -o t1.i 预处理gcc -S t1.i -o t1.s 转成汇编语言gcc -c t1.s -o t1.o 转成机器码gcc t1.o -o t1.exe 链接 直接使用 ...
- Linux下打包、压缩和解压
命令使用:tar 主选项+辅选项 压缩后文件名 要压缩的文件 -c 建立压缩文件(常用) -x 解压(常用) -t 查看压缩文件(常用) -r 向压缩文件末尾追加文件 -u 更新一个压缩包中的文 ...
- 【华科考研机试题】最长&最短文本
题目 输入多行字符串,请按照原文本中的顺序输出其中最短和最长的字符串,如果最短和最长的字符串不止一个,请全部输出. 解题思路 1.输入所有字符串(有空格不另算字符串). 2.将char*字符串转换成s ...