Step 7. ContentProviderProxy.query
这个函数定义在frameworks/base/core/java/android/content/ContentProviderNative.java文件中:
-
final class ContentProviderProxy implements IContentProvider {
-
......
-
-
public Cursor query(Uri url, String[] projection, String selection,
-
String[] selectionArgs, String sortOrder) throws RemoteException {
-
//TODO make a pool of windows so we can reuse memory dealers
-
CursorWindow window = new CursorWindow(false /* window will be used remotely */);
-
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
-
IBulkCursor bulkCursor = bulkQueryInternal(
-
url, projection, selection, selectionArgs, sortOrder,
-
adaptor.getObserver(), window,
-
adaptor);
-
if (bulkCursor == null) {
-
return null;
-
}
-
return adaptor;
-
}
-
-
......
-
}
这个函数首先会创建一个CursorWindow对象,前面已经说过,这个CursorWindow对象包含了一块匿名共享内存,它的作用是把这块匿名共享内存通过Binder进程间通信机制传给Content Proivder,好让Content Proivder在里面返回所请求的数据。下面我们就先看看这个CursorWindow对象的创建过程,重点关注它是如何在内部创建匿名共享内存的,然后再回过头来看看它调用bulkQueryInternal函数来做了些什么事情。
CursorWindow类定义在frameworks/base/core/java/android/database/CursorWindow.java文件中,我们来看看它的构造函数的实现:
-
public class CursorWindow extends SQLiteClosable implements Parcelable {
-
......
-
-
private int nWindow;
-
-
......
-
-
public CursorWindow(boolean localWindow) {
-
......
-
-
native_init(localWindow);
-
}
-
-
......
-
}
它主要调用本地方法native_init来执行初始化的工作,主要就是创建匿名共享内存了,传进来的参数localWindow为false,表示这个匿名共享内存只能通过远程调用来访问,即前面我们所说的,通过Content Proivder返回来的Cursor接口来访问这块匿名共享内存里面的数据。
Step 8. CursorWindow.native_init
这是一个JNI方法,它对应定义在frameworks/base/core/jni/android_database_CursorWindow.cpp文件中的native_init_empty函数:
-
static JNINativeMethod sMethods[] =
-
{
-
/* name, signature, funcPtr */
-
{"native_init", "(Z)V", (void *)native_init_empty},
-
......
-
};
函数native_init_empty的定义如下所示:
-
static void native_init_empty(JNIEnv * env, jobject object, jboolean localOnly)
-
{
-
......
-
-
CursorWindow * window;
-
-
window = new CursorWindow(MAX_WINDOW_SIZE);
-
......
-
-
if (!window->initBuffer(localOnly)) {
-
......
-
}
-
-
......
-
SET_WINDOW(env, object, window);
-
}
这个函数在C++层创建了一个CursorWindow对象,然后通过调用SET_WINDOW宏来把这个C++层的CursorWindow对象与Java层的CursorWindow对象关系起来:
-
#define SET_WINDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window))
这里的gWindowField即定义为Java层的CursorWindow对象中的nWindow成员变量:
-
static jfieldID gWindowField;
-
-
......
-
-
int register_android_database_CursorWindow(JNIEnv * env)
-
{
-
jclass clazz;
-
-
clazz = env->FindClass("android/database/CursorWindow");
-
......
-
-
gWindowField = env->GetFieldID(clazz, "nWindow", "I");
-
-
......
-
}
这种用法在Android应用程序框架层中非常普遍。
下面我们重点关注C++层的CursorWindow对象的initBuffer函数的实现。
Step 9. CursorWindow.initBuffer
C++层的CursorWindow类定义在frameworks/base/core/jni/CursorWindow.cpp文件中:
-
bool CursorWindow::initBuffer(bool localOnly)
-
{
-
......
-
-
sp<MemoryHeapBase> heap;
-
heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow");
-
if (heap != NULL) {
-
mMemory = new MemoryBase(heap, 0, mMaxSize);
-
if (mMemory != NULL) {
-
mData = (uint8_t *) mMemory->pointer();
-
if (mData) {
-
mHeader = (window_header_t *) mData;
-
mSize = mMaxSize;
-
-
......
-
}
-
}
-
......
-
} else {
-
......
-
}
-
}
这里我们就可以很清楚地看到,在CursorWindow类的内部有一个成员变量mMemory,它的类型是MemoryBase。MemoryBase类为我们封装了匿名共享内存的访问以及在进程间的传输等问题,具体可以参考前面一篇文章
Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析
,这里就不再详述了。
通过Step 8和Step 9两步,用来在第三方应用程序和Content Provider之间传输数据的媒介就准备好了,我们回到Step 7中,看看系统是如何把这个匿名共享存传递给Content Provider使用的。在Step 7中,最后调用bulkQueryInternal函数来进一步操作。
Step 10. ContentProviderProxy.bulkQueryInternal
这个函数定义在frameworks/base/core/java/android/content/ContentProviderNative.java文件中:
-
final class ContentProviderProxy implements IContentProvider
-
{
-
......
-
-
private IBulkCursor bulkQueryInternal(
-
Uri url, String[] projection,
-
String selection, String[] selectionArgs, String sortOrder,
-
IContentObserver observer, CursorWindow window,
-
BulkCursorToCursorAdaptor adaptor) throws RemoteException {
-
Parcel data = Parcel.obtain();
-
Parcel reply = Parcel.obtain();
-
-
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(observer.asBinder());
-
window.writeToParcel(data, 0);
-
-
// Flag for whether or not we want the number of rows in the
-
// cursor and the position of the "_id" column index (or -1 if
-
// non-existent). Only to be returned if binder != null.
-
final boolean wantsCursorMetadata = (adaptor != null);
-
data.writeInt(wantsCursorMetadata ? 1 : 0);
-
-
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
-
-
DatabaseUtils.readExceptionFromParcel(reply);
-
-
IBulkCursor bulkCursor = null;
-
IBinder bulkCursorBinder = reply.readStrongBinder();
-
if (bulkCursorBinder != null) {
-
bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder);
-
-
if (wantsCursorMetadata) {
-
int rowCount = reply.readInt();
-
int idColumnPosition = reply.readInt();
-
if (bulkCursor != null) {
-
adaptor.set(bulkCursor, rowCount, idColumnPosition);
-
}
-
}
-
}
-
-
data.recycle();
-
reply.recycle();
-
-
return bulkCursor;
-
}
-
-
......
-
}
这个函数有点长,不过它的逻辑很简单,就是把查询参数都写到一个Parcel对象data中去,然后通过下面Binder进程间通信机制把查询请求传给Content Provider处理:
-
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
从这个Binder调用返回以后,就会得到一个IBulkCursor接口,它是一个Binder引用,实际是指向在Content Provider这一侧创建的一个CursorToBulkCursorAdaptor对象,后面我们将会看到。有了这个IBulkCursor接口之后,我们就可以通过Binder进程间调用来访问从Content Provider中查询得到的数据了。这个IBulkCursor接口最终最设置了上面Step 7中创建的BulkCursorToCursorAdaptor对象adaptor中去:
-
adaptor.set(bulkCursor, rowCount, idColumnPosition);
BulkCursorToCursorAdaptor类实现了Cursor接口,因此,我们可以通过Curosr接口来访问这些查询得到的共享数据。
在前面把查询参数写到Parcel对象data中去的过程中,有两个步骤是比较重要的,分别下面这段执行语句:
-
window.writeToParcel(data, 0);
-
-
// Flag for whether or not we want the number of rows in the
-
// cursor and the position of the "_id" column index (or -1 if
-
// non-existent). Only to be returned if binder != null.
-
final boolean wantsCursorMetadata = (adaptor != null);
-
data.writeInt(wantsCursorMetadata ? 1 : 0);
调用window.writeToParcel是把window对象内部的匿名共享内存块通过Binder进程间通信机制传输给Content Provider来使用;而当传进来的参数adaptor不为null时,就会往data中写入一个整数1,表示让Content Provider把查询得到数据的元信息一起返回来,例如数据的行数、数据行的ID列的索引位置等信息,这个整数值会促使Content Provider把前面说的IBulkCursor接口返回给第三方应用程序之前,真正执行一把数据库查询操作,后面我们将看到这个过程。
现在,我们重点来关注一下CursorWindow类的writeToParcel函数,看看它是如何把它内部的匿名共享内存对象写到数据流data中去的。
本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966999,如需转载请自行联系原作者