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,如需转载请自行联系原作者
目录
相关文章
|
2月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
252 4
|
1月前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
70 14
|
1月前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
1月前
|
搜索推荐 前端开发 测试技术
打造个性化安卓应用:从设计到开发的全面指南
在这个数字时代,拥有一个定制的移动应用不仅是一种趋势,更是个人或企业品牌的重要延伸。本文将引导你通过一系列简单易懂的步骤,从构思你的应用理念开始,直至实现一个功能齐全的安卓应用。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你提供必要的工具和知识,帮助你将创意转化为现实。
|
1月前
|
Java Android开发 开发者
探索安卓开发:构建你的第一个“Hello World”应用
在安卓开发的浩瀚海洋中,每个新手都渴望扬帆起航。本文将作为你的指南针,引领你通过创建一个简单的“Hello World”应用,迈出安卓开发的第一步。我们将一起搭建开发环境、了解基本概念,并编写第一行代码。就像印度圣雄甘地所说:“你必须成为你希望在世界上看到的改变。”让我们一起开始这段旅程,成为我们想要见到的开发者吧!
43 0
|
2月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
2月前
|
缓存 前端开发 Android开发
安卓开发中的自定义视图:从零到英雄
【10月更文挑战第42天】 在安卓的世界里,自定义视图是一块画布,让开发者能够绘制出独一无二的界面体验。本文将带你走进自定义视图的大门,通过深入浅出的方式,让你从零基础到能够独立设计并实现复杂的自定义组件。我们将探索自定义视图的核心概念、实现步骤,以及如何优化你的视图以提高性能和兼容性。准备好了吗?让我们开始这段创造性的旅程吧!
43 1
|
2月前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
1天前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
1月前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
65 19

热门文章

最新文章