Android应用程序组件Content Provider在应用程序之间共享数据的原理分析(2)-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

Android应用程序组件Content Provider在应用程序之间共享数据的原理分析(2)

简介:
 Step 7. ContentProviderProxy.query
      这个函数定义在frameworks/base/core/java/android/content/ContentProviderNative.java文件中:
  1. final class ContentProviderProxy implements IContentProvider {  
  2.     ......  
  3.   
  4.     public Cursor query(Uri url, String[] projection, String selection,  
  5.             String[] selectionArgs, String sortOrder) throws RemoteException {  
  6.         //TODO make a pool of windows so we can reuse memory dealers  
  7.         CursorWindow window = new CursorWindow(false /* window will be used remotely */);  
  8.         BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();  
  9.         IBulkCursor bulkCursor = bulkQueryInternal(  
  10.             url, projection, selection, selectionArgs, sortOrder,  
  11.             adaptor.getObserver(), window,  
  12.             adaptor);  
  13.         if (bulkCursor == null) {  
  14.             return null;  
  15.         }  
  16.         return adaptor;  
  17.     }  
  18.   
  19.     ......  
  20. }  
        这个函数首先会创建一个CursorWindow对象,前面已经说过,这个CursorWindow对象包含了一块匿名共享内存,它的作用是把这块匿名共享内存通过Binder进程间通信机制传给Content Proivder,好让Content Proivder在里面返回所请求的数据。下面我们就先看看这个CursorWindow对象的创建过程,重点关注它是如何在内部创建匿名共享内存的,然后再回过头来看看它调用bulkQueryInternal函数来做了些什么事情。
        CursorWindow类定义在frameworks/base/core/java/android/database/CursorWindow.java文件中,我们来看看它的构造函数的实现:
  1. public class CursorWindow extends SQLiteClosable implements Parcelable {  
  2.     ......  
  3.   
  4.     private int nWindow;  
  5.   
  6.     ......  
  7.   
  8.     public CursorWindow(boolean localWindow) {  
  9.         ......  
  10.   
  11.         native_init(localWindow);  
  12.     }  
  13.   
  14.     ......  
  15. }  
 
        它主要调用本地方法native_init来执行初始化的工作,主要就是创建匿名共享内存了,传进来的参数localWindow为false,表示这个匿名共享内存只能通过远程调用来访问,即前面我们所说的,通过Content Proivder返回来的Cursor接口来访问这块匿名共享内存里面的数据。
        Step 8. CursorWindow.native_init
        这是一个JNI方法,它对应定义在frameworks/base/core/jni/android_database_CursorWindow.cpp文件中的native_init_empty函数:
  1. static JNINativeMethod sMethods[] =  
  2. {  
  3.      /* name, signature, funcPtr */  
  4.     {"native_init""(Z)V", (void *)native_init_empty},  
  5.     ......  
  6. };  
         函数native_init_empty的定义如下所示:
  1. static void native_init_empty(JNIEnv * env, jobject object, jboolean localOnly)  
  2. {  
  3.     ......  
  4.   
  5.     CursorWindow * window;  
  6.   
  7.     window = new CursorWindow(MAX_WINDOW_SIZE);  
  8.     ......  
  9.   
  10.     if (!window->initBuffer(localOnly)) {  
  11.         ......  
  12.     }  
  13.   
  14.     ......  
  15.     SET_WINDOW(env, object, window);  
  16. }  
         这个函数在C++层创建了一个CursorWindow对象,然后通过调用SET_WINDOW宏来把这个C++层的CursorWindow对象与Java层的CursorWindow对象关系起来:
  1. #define SET_WINDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window))  
         这里的gWindowField即定义为Java层的CursorWindow对象中的nWindow成员变量:
  1. static jfieldID gWindowField;  
  2.   
  3. ......  
  4.   
  5. int register_android_database_CursorWindow(JNIEnv * env)  
  6. {  
  7.     jclass clazz;  
  8.   
  9.     clazz = env->FindClass("android/database/CursorWindow");  
  10.     ......  
  11.   
  12.     gWindowField = env->GetFieldID(clazz, "nWindow""I");  
  13.   
  14.     ......  
  15. }  
        这种用法在Android应用程序框架层中非常普遍。
 
        下面我们重点关注C++层的CursorWindow对象的initBuffer函数的实现。
        Step 9. CursorWindow.initBuffer
        C++层的CursorWindow类定义在frameworks/base/core/jni/CursorWindow.cpp文件中:
  1. bool CursorWindow::initBuffer(bool localOnly)  
  2. {  
  3.     ......  
  4.   
  5.     sp<MemoryHeapBase> heap;  
  6.     heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow");  
  7.     if (heap != NULL) {  
  8.         mMemory = new MemoryBase(heap, 0, mMaxSize);  
  9.         if (mMemory != NULL) {  
  10.             mData = (uint8_t *) mMemory->pointer();  
  11.             if (mData) {  
  12.                 mHeader = (window_header_t *) mData;  
  13.                 mSize = mMaxSize;  
  14.   
  15.                 ......  
  16.             }  
  17.         }  
  18.         ......  
  19.     } else {  
  20.         ......  
  21.     }  
  22. }  
        这里我们就可以很清楚地看到,在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文件中:

  1. final class ContentProviderProxy implements IContentProvider   
  2. {   
  3.     ......   
  4.    
  5.     private IBulkCursor bulkQueryInternal(   
  6.             Uri url, String[] projection,   
  7.             String selection, String[] selectionArgs, String sortOrder,   
  8.             IContentObserver observer, CursorWindow window,   
  9.             BulkCursorToCursorAdaptor adaptor) throws RemoteException {   
  10.         Parcel data = Parcel.obtain();   
  11.         Parcel reply = Parcel.obtain();   
  12.    
  13.         data.writeInterfaceToken(IContentProvider.descriptor);   
  14.    
  15.         url.writeToParcel(data, 0);   
  16.         int length = 0;   
  17.         if (projection != null) {   
  18.             length = projection.length;   
  19.         }   
  20.         data.writeInt(length);   
  21.         for (int i = 0; i < length; i++) {   
  22.             data.writeString(projection[i]);   
  23.         }   
  24.         data.writeString(selection);   
  25.         if (selectionArgs != null) {   
  26.             length = selectionArgs.length;   
  27.         } else {   
  28.             length = 0;   
  29.         }   
  30.         data.writeInt(length);   
  31.         for (int i = 0; i < length; i++) {   
  32.             data.writeString(selectionArgs[i]);   
  33.         }   
  34.         data.writeString(sortOrder);   
  35.         data.writeStrongBinder(observer.asBinder());   
  36.         window.writeToParcel(data, 0);   
  37.    
  38.         // Flag for whether or not we want the number of rows in the   
  39.         // cursor and the position of the "_id" column index (or -1 if   
  40.         // non-existent).  Only to be returned if binder != null.   
  41.         final boolean wantsCursorMetadata = (adaptor != null);   
  42.         data.writeInt(wantsCursorMetadata ? 1 : 0);   
  43.    
  44.         mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);   
  45.    
  46.         DatabaseUtils.readExceptionFromParcel(reply);   
  47.    
  48.         IBulkCursor bulkCursor = null;   
  49.         IBinder bulkCursorBinder = reply.readStrongBinder();   
  50.         if (bulkCursorBinder != null) {   
  51.             bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder);   
  52.    
  53.             if (wantsCursorMetadata) {   
  54.                 int rowCount = reply.readInt();   
  55.                 int idColumnPosition = reply.readInt();   
  56.                 if (bulkCursor != null) {   
  57.                     adaptor.set(bulkCursor, rowCount, idColumnPosition);   
  58.                 }   
  59.             }   
  60.         }   
  61.    
  62.         data.recycle();   
  63.         reply.recycle();   
  64.    
  65.         return bulkCursor;   
  66.     }   
  67.    
  68.     ......   
  69. }   
     这个函数有点长,不过它的逻辑很简单,就是把查询参数都写到一个Parcel对象data中去,然后通过下面Binder进程间通信机制把查询请求传给Content Provider处理: 
  1. mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);  
      从这个Binder调用返回以后,就会得到一个IBulkCursor接口,它是一个Binder引用,实际是指向在Content Provider这一侧创建的一个CursorToBulkCursorAdaptor对象,后面我们将会看到。有了这个IBulkCursor接口之后,我们就可以通过Binder进程间调用来访问从Content Provider中查询得到的数据了。这个IBulkCursor接口最终最设置了上面Step 7中创建的BulkCursorToCursorAdaptor对象adaptor中去:
 
 
  1. adaptor.set(bulkCursor, rowCount, idColumnPosition);  
      BulkCursorToCursorAdaptor类实现了Cursor接口,因此,我们可以通过Curosr接口来访问这些查询得到的共享数据。
 
      在前面把查询参数写到Parcel对象data中去的过程中,有两个步骤是比较重要的,分别下面这段执行语句:
  1. window.writeToParcel(data, 0);  
  2.   
  3. // Flag for whether or not we want the number of rows in the  
  4. // cursor and the position of the "_id" column index (or -1 if  
  5. // non-existent).  Only to be returned if binder != null.  
  6. final boolean wantsCursorMetadata = (adaptor != null);  
  7. 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,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: