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,如需转载请自行联系原作者
目录
相关文章
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
1618 4
|
11月前
|
安全 数据库 Android开发
在Android开发中实现两个Intent跳转及数据交换的方法
总结上述内容,在Android开发中,Intent不仅是活动跳转的桥梁,也是两个活动之间进行数据交换的媒介。运用Intent传递数据时需注意数据类型、传输大小限制以及安全性问题的处理,以确保应用的健壯性和安全性。
675 11
|
安全 Android开发 Kotlin
Android经典实战之SurfaceView原理和实践
本文介绍了 `SurfaceView` 这一强大的 UI 组件,尤其适合高性能绘制任务,如视频播放和游戏。文章详细讲解了 `SurfaceView` 的原理、与 `Surface` 类的关系及其实现示例,并强调了使用时需注意的线程安全、生命周期管理和性能优化等问题。
807 8
|
开发工具 Android开发 开发者
Android平台如何不推RTMP|不发布RTSP流|不实时录像|不回传GB28181数据时实时快照?
本文介绍了一种在Android平台上实现实时截图快照的方法,尤其适用于无需依赖系统接口的情况,如在RTMP推送、RTSP服务或GB28181设备接入等场景下进行截图。通过底层模块(libSmartPublisher.so)实现了截图功能,封装了`SnapShotImpl.java`类来管理截图流程。此外,提供了关键代码片段展示初始化SDK实例、执行截图、以及在Activity销毁时释放资源的过程。此方案还考虑到了快照数据的灵活处理需求,符合GB/T28181-2022的技术规范。对于寻求更灵活快照机制的开发者来说,这是一个值得参考的设计思路。
448 1
|
存储 XML Java
Android 文件数据储存之内部储存 + 外部储存
简介:本文详细介绍了Android内部存储与外部存储的使用方法及核心原理。内部存储位于手机内存中,默认私有,适合存储SharedPreferences、SQLite数据库等重要数据,应用卸载后数据会被清除。外部存储包括公共文件和私有文件,支持SD卡或内部不可移除存储,需申请权限访问。文章通过代码示例展示了如何保存、读取、追加、删除文件以及将图片保存到系统相册的操作,帮助开发者理解存储机制并实现相关功能。
2930 2
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
1001 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
缓存 Java 数据库
Android的ANR原理
【10月更文挑战第18天】了解 ANR 的原理对于开发高质量的 Android 应用至关重要。通过合理的设计和优化,可以有效避免 ANR 的发生,提升应用的性能和用户体验。
838 56
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
564 3
|
编解码 前端开发 Android开发
Android经典实战之TextureView原理和高级用法
本文介绍了 `TextureView` 的原理和特点,包括其硬件加速渲染的优势及与其他视图叠加使用的灵活性,并提供了视频播放和自定义绘制的示例代码。通过合理管理生命周期和资源,`TextureView` 可实现高效流畅的图形和视频渲染。
1190 12
|
ARouter 测试技术 API
Android经典面试题之组件化原理、优缺点、实现方法?
本文介绍了组件化在Android开发中的应用,详细阐述了其原理、优缺点及实现方式,包括模块化、接口编程、依赖注入、路由机制等内容,并提供了具体代码示例。
506 3

热门文章

最新文章