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类为我们封装了匿名共享内存的访问以及在进程间的传输等问题,具体可以参考前面一篇文章 ,这里就不再详述了。
通过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中去的。