7.4 Cursor的query函数的实现分析

本节将分析CP中另一个比较复杂的知识点,即query函数的实现。下面从CR的query函数开始介绍,其代码如下:

[—>ContentResolver.java:query]


/*

注意query函数中的参数,它们组合后得到的SQL语句如下(方括号中的语句为笔者添加的注释)

SELECT projection指定的列名[如果projection为null,则使用“*”]

FROM表名[由目标CP根据uri参数设置]WHERE

(selection)[如果selection中有通配符,则具体参数由selectionArgs指定]

ORDERYED BY sortOrder

*/

public final Cursor query(Uri uri, String[]projection,

String selection, String[]selectionArgs, String sortOrder){

/*

根据前面的分析,下面这个函数返回的provider的真实类型是ContentProviderProxy,

对应的Bn端对象的真实类型是CP的内部类Transport。本次执行query的

目标CP是MediaProvider

*/

IContentProvider provider=acquireProvider(uri);

//来看下面的代码

try{

long startTime=SystemClock.uptimeMillis();

//①调用远端进程的query函数

Cursor qCursor=provider.query(uri, projection,

selection, selectionArgs, sortOrder);

if(qCursor==null){

//如果返回的结果为空,则释放provider,这个函数的作用在7.2.3节介绍过

releaseProvider(provider);

return null;

}

//②计算查询结果包含的数据项条数,结果保存在qCursor的内部变量中

qCursor.getCount();

long durationMillis=SystemClock.uptimeMillis()-startTime;

//③最终返回给客户端一个游标对象,其真实数据类型为CursorWrapperInner

return new CursorWrapperInner(qCursor, provider);

}


上面这段代码揭示了CR的query函数的工作流程:

调用远程CP的query函数,返回一个Cursor类型的对象qCursor。

该函数最终返给客户端的是一个CursorWrapperInner类型的对象。

Cursor和CursorWrappperInner这两个新出现的数据类型严重干扰了我们的思考。暂且不管它们,先来分析以上代码中列出的几个关键点函数。首先要分析的是第一个关键点函数query。

7.4.1 提取query关键点

1.客户端query关键点

按以前的分析习惯,碰到Binder调用时会马上转入服务端(即Bn端)去分析,但是这个思路在query函数中行不通。为什么?来看IContentProvider Bp端的query函数,它定义在ContentProviderProxy中,代码如下:

[—>ContentProviderNative.java:ContentProviderProxy.query]


public Cursor query(Uri url, String[]projection, String selection,

String[]selectionArgs, String sortOrder)throws RemoteException{

//①构造一个BulkCursorToCursorAdaptor对象

BulkCursorToCursorAdaptor adaptor=new BulkCursorToCursorAdaptor();

Parcel data=Parcel.obtain();

Parcel reply=Parcel.obtain();

try{

data.writeInterfaceToken(IContentProvider.descriptor);

……//将参数打包到data请求包中

//②adaptor.getObserver()返回一个IContentObserver类型的对象,把它

//也打包到data请求包中。ContentObserver相关的知识留到第8章再分析

data.writeStrongBinder(adaptor.getObserver().asBinder());

//发送请求给远端的Bn端

mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply,0);

DatabaseUtils.readExceptionFromParcel(reply);

//③从回复包中得到一个IBulkCursor类型的对象

IBulkCursor bulkCursor=

BulkCursorNative.asInterface(reply.readStrongBinder());

if(bulkCursor!=null){

int rowCount=reply.readInt();

int idColumnPosition=reply.readInt();

boolean wantsAllOnMoveCalls=reply.readInt()!=0;

//④调用adaptor的initialize函数

adaptor.initialize(bulkCursor, rowCount,

idColumnPosition, wantsAllOnMoveCalls);

}……

return adaptor;

}……

finally{

data.recycle();

reply.recycle();

}

}


从以上代码中可发现,ContentProviderProxy query函数中还大有文章,其中一共列出了4个关键点。最令人头疼的是其中新出现的两个类BulkCursorToCursorAdaptor和IBulkCursor。此处不必急于分析它们。

我们再到服务端去提取query函数的关键点。

2.服务端query关键点

根据第2章对Binder的介绍,客户端发来的请求先在Bn端的onTransact中得到处理,代码如下:

[—>ContentProviderNative.java:onTransact]


public boolean onTransact(int code, Parcel data, Parcel reply, int flags)

throws RemoteException{

try{

switch(code){

case QUERY_TRANSACTION://处理query请求

{

data.enforceInterface(IContentProvider.descriptor);

Uri url=Uri.CREATOR.createFromParcel(data);

……//从请求包中提取参数

//⑤创建ContentObserver Binder通信的Bp端

IContentObserver observer=IContentObserver.Stub.asInterface(

data.readStrongBinder());

//⑥调用MediaProvider实现的query函数。Cursor是一个接口类,那么变量

//cursor的真实类型是什么?

Cursor cursor=query(url, projection, selection, selectionArgs,

sortOrder);

if(cursor!=null){

//⑦创建一个CursorToBulkCursorAdaptor类型的对象

CursorToBulkCursorAdaptor adaptor=new

CursorToBulkCursorAdaptor(

cursor, observer, getProviderName());

final IBinder binder=adaptor.asBinder();

//⑧返回结果集所含记录项的条数,这个函数看起来极不起眼,但却非常关键

final int count=adaptor.count();

//返回名为"_id"的列在结果集中的索引位置,该列由数据库在创建表时自动添加

final int index=BulkCursorToCursorAdaptor.findRowIdColumnIndex(

adaptor.getColumnNames());

//wantsAllOnMoveCalls一般为false,读者阅读完本章后再自行分析它

final boolean wantsAllOnMoveCalls=

adaptor.getWantsAllOnMoveCalls();

reply.writeNoException();

//将binder的信息写到reply回复包中

reply.writeStrongBinder(binder);

reply.writeInt(count);//将结果集包含的记录项行数返回给客户端

reply.writeInt(index);

reply.writeInt(wantsAllOnMoveCalls?1:0);

}……

return true;

}……//其他情况处理

……

}


和客户端对应,服务端的query处理也比较复杂,其中的“拦路虎”仍是新出现的几种数据类型。

在扫清这些“拦路虎”之前,应先把客户端和服务端query调用的关键点总结一下。

3.提取query关键点总结

我们提取query两端的调用关键点,如图7-5所示。

7.4 Cursor的query函数的实现分析 - 图1

图 7-5 query调用关键点示意

再来总结一下前面提到的几个“拦路虎”,它们分别是:

客户端创建的BulkCursorToCursorAdaptor,以及从服务端query后得到的IBulkCursor。

服务端创建的CursorToCursorAdaptor,以及从子类query函数返回的Cursor。

从名字上看,这几个类都和Cursor有关,所以有必要先搞清MediaProvider query返回的Cursor到底是什么。

注意 图7-5借用了UML的序列图来展示query调用顺序,其中ContentProvider(CP)框和MediaProvider框代表同一个对象。另外,图7-5中的调用函数编号并不完全对应代码中的关键函数调用编号。