ImageVerifierCode 换一换
格式:DOC , 页数:27 ,大小:345KB ,
资源ID:11253435      下载积分:10 金币
快捷注册下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

开通VIP
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.zixin.com.cn/docdown/11253435.html】到电脑端继续下载(重复下载【60天内】不扣币)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

开通VIP折扣优惠下载文档

            查看会员权益                  [ 下载后找不到文档?]

填表反馈(24小时):  下载求助     关注领币    退款申请

开具发票请登录PC端进行申请

   平台协调中心        【在线客服】        免费申请共赢上传

权利声明

1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。

注意事项

本文(Android应用程序组件Content-Provider在应用程序之间共享数据的原理分析.doc)为本站上传会员【仙人****88】主动上传,咨信网仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知咨信网(发送邮件至1219186828@qq.com、拔打电话4009-655-100或【 微信客服】、【 QQ客服】),核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载【60天内】不扣币。 服务填表

Android应用程序组件Content-Provider在应用程序之间共享数据的原理分析.doc

1、Android应用程序组件Content Provider在应用程序之间共享数据的原理分析 在Android系统中,不同的应用程序是不能直接读写对方的数据文件的,如果它们想共享数据的话,只能通过Content Provider组件来实现。那么,Content Provider组件又是如何突破应用程序边界权限控制来实现在不同的应用程序之间共享数据的呢?在前面的文章中,我们已经简要介绍过它是通过Binder进程间通信机制以及匿名共享内存机制来实现的,在本文中,我们将详细分析它的数据共享原理。 Android应用程序之间不能直接访问对方的数据文件的障碍在于每一个应用程序都有自

2、己的用户ID,而每一个应用程序所创建的文件的读写权限都是只赋予给自己所属的用户,因此,就限制了应用程序之间相互读写数据的操作,关于Android应用程序的权限问题,具体可以参考前面一篇文章 。通过前面 等一系列文章的学习,我们知道,Binder进程间通信机制可以突破了以应用程序为边界的权限控制来实现在不同应用程序之间传输数据,而Content Provider组件在不同应用程序之间共享数据正是基于Binder进程间通信机制来实现的。虽然Binder进程间通信机制突破了以应用程序为边界的权限控制,但是它是安全可控的,因为数据的访问接口是由数据的所有者来提供的,换句话来说,就是数据提供

3、方可以在接口层来实现安全控制,决定哪些数据是可以读,哪些数据可以写。虽然Content Provider组件本身也提供了读写权限控制,但是它的控制粒度是比较粗的,如果有需要,我们还是可以在接口访问层做更细粒度的权限控制以达到数据安全的目的。 Binder进程间通信机制虽然打通了应用程序之间共享数据的通道,但是还有一个问题需要解决,那就是数据要以什么来作来媒介来传输。我们知道,应用程序采用Binder进程间通信机制进行通信时,要传输的数据都是采用函数参数的形式进行的,对于一般的进程间调来来说,这是没有问题的,然而,对于应用程序之间的共享数据来说,它们的数据量可能是非常大的,

4、如果还是简单的用函数参数的形式来传递,效率就会比较低下。通过前面 等一系列文章的学习,我们知道,在应用程序进程之间以匿名共享内存的方式来传输数据效率是非常高的,因为它们之间只需要传递一个文件描述符就可以了。因此,Content Provider组件在不同应用程序之间传输数据正是基于匿名共享内存机制来实现的。 在继续分析Content Provider组件在不同应用程序之间共享数据的原理之前,我们假设应用程序之间需要共享的数据是保存在数据库(SQLite)中的,因此,接下来的分析都是基于SQLite数据库游标(SQLiteCursor)来进行的。SQLiteCurso

5、r在共享数据的传输过程中发挥着重要的作用,因此,我们先来它和其它相关的类的关系图,如下所示: 首先在第三方应用程序这一侧,当它需要访问Content Provider中的数据时,它会在本进程中创建一个CursorWindow对象,它在内部创建了一块匿名共享内存,同时,它实现了Parcel接口,因此它可以在进程间传输。接下来第三方应用程序把这个CursorWindow对象(连同它内部的匿名共享内存文件描述符)通过Binder进程间调用传输到Content Provider这一侧。这个匿名共享内存文件描述符传输到Binder驱动程序的时候,Binder驱动程序就会在目标进程(即Content

6、 Provider所在的进程)中创建另一个匿名共享文件描述符,指向前面已经创建好的匿名共享内存,因此,就实现了在两个进程中共享同一块匿名内存,这个过程具体可以参考 一文。 在Content Provider这一侧,利用在Binder驱动程序为它创建好的这个匿名共享内存文件描述符,在本进程中创建了一个CursorWindow对象。现在,Content Provider开始要从本地中从数据库中查询第三方应用程序想要获取的数据了。Content Provider首先会创建一个SQLiteCursor对象,即SQLite数据库游标对象,它继承了AbstractWindowe

7、dCursor类,后者又继承了AbstractCursor类,而AbstractCursor类又实现了CrossProcessCursor和Cursor接口。其中,最重要的是在AbstractWindowedCursor类中,有一个成员变量mWindow,它的类型为CursorWindow,这个成员变量是通过AbstractWindowedCursor的子类SQLiteCursor的setWindow成员函数来设置的。这个SQLiteCursor对象设置好了父类AbstractWindowedCursor类的mWindow成员变量之后,它就具有传输数据的能力了,因为这个mWindow对象内部包

8、含一块匿名共享内存。此外,这个SQLiteCursor对象的内部有两个成员变量,一个是SQLite数据库对象mDatabase,另外一个是SQLite数据库查询对象mQuery。SQLite数据库查询对象mQuery的类型为SQLiteQuery,它继承了SQLiteProgram类,后者又继承了SQLiteClosable类。SQLiteProgram类代表一个数据库存查询计划,它的成员变量mCompiledSql包含了一个已经编译好的SQL查询语句,SQLiteCursor对象就是利用这个编译好的SQL查询语句来获得数据的,但是它并不是马上就去获取数据的,而是等到需要时才去获取。

9、 那么,要等到什么时候才会需要获取数据呢?一般来说,如果第三方应用程序在请求Content Provider返回数据时,如果指定了要返回关于这些数据的元信息时,例如数据条目的数量,那么Content Provider在把这个SQLiteCursor对象返回给第三方应用程序之前,就会去获取数据,因为只有获取了数据之后,才知道数据条目的数量是多少。SQLiteCursor对象通过调用成员变量mQuery的fillWindow成员函数来把从SQLite数据库中查询得到的数据保存其父类AbstractWindowedCursor的成员变量mWindow中去,即保存到第三方应用程序创建的这块

10、匿名共享内存中去。如果第三方应用程序在请求Content Provider返回数据时,没有指定要返回关于这些数据的元信息,那么,就要等到第三方应用程序首次调用这个从Content Provider处返回的SQLiteCursor对象的数据获取方法时,才会真正执行从数据库存中查询数据的操作,例如调用了SQLiteCursor对象的getCount或者moveToFirst成员函数时。这是一种数据懒加载机制,需要的时候才去加载,这样就提高了数据传输过程中的效率。 上面说到,Content Provider向第三方应用程序返回的数据实际上是一个SQLiteCursor对象,那么

11、这个SQLiteCursor对象是如何传输到第三方应用程序的呢?因为它本身并不是一个Binder对象,我们需要对它进行适配一下。首先,Content Provider会根据这个SQLiteCursor对象来创建一个CursorToBulkCursorAdaptor适配器对象,这个适配器对象是一个Binder对象,因此,它可以在进程间传输,同时,它实现了IBulkCursor接口。Content Provider接着就通过Binder进程间通信机制把这个CursorToBulkCursorAdaptor对象返回给第三方应用程序,第三方应用程序得到了这个CursorToBulkCursorAda

12、ptor之后,再在本地创建一个BulkCursorToCursorAdaptor对象,这个BulkCursorToCursorAdaptor对象的继承结构和SQLiteCursor对象是一样的,不过,它没有设置父类AbstractWindowedCursor的mWindow成员变量,因此,它只可以通过它内部的CursorToBulkCursorAdaptor对象引用来访问匿名共享内存中的数据,即通过访问Content Provider这一侧的SQLiteCursor对象来访问共享数据。 上面描述的数据共享模型还是比较复杂的,一下子理解不了也不要紧,下面我们还会结合第三方应

13、用程序和Content Provider传输共享数据的完整过程来进一步分析Content Provider的数据共享原理,到时候再回过头来看这个数据共享模型就会清晰很多了。在接下来的内容中,我们就继续以 一文的例子来分析Content Provider在不同应用程序之间共享数据的原理。在 这篇文章介绍的应用程序Article中,它的主窗口MainActivity是通过调用它的内部ArticlesAdapter对象的getArticleByPos成员函数来把ArticlesProvider中的文章信息条目一条一条地取回来显示在ListView中的,在这篇文章中,我们就从Article

14、sAdapter类的getArticleByPos函数开始,一步一步地分析第三方应用程序Article从ArticlesProvider这个Content Provider中获取数据的过程。同样,我们先来看看这个过程的序列图,然后再详细分析每一个步骤: 这个函数定义在前面一篇文章 介绍的应用程序Artilce源代码工程目录下,在文件为packages/experimental/Article/src/shy/luo/article/ArticlesAdapter.Java中: [java] view plain copy 在CODE上查看代码片派生到我的代码片 publ

15、ic class ArticlesAdapter { ...... private ContentResolver resolver = null; public ArticlesAdapter(Context context) { resolver = context.getContentResolver(); } ...... public Article getArticleByPos(int pos) { Uri uri = C

16、ontentUris.withAppendedId(Articles.CONTENT_POS_URI, pos); String[] projection = new String[] { Articles.ID, Articles.TITLE, Articles.ABSTRACT, Articles.URL }; Cursor cursor = resolver.query(uri, proj

17、ection, null, null, Articles.DEFAULT_SORT_ORDER); if (!cursor.moveToFirst()) { return null; } int id = cursor.getInt(0); String title = cursor.getString(1); String abs = cursor.getString(2); String url = cursor.getS

18、tring(3); return new Article(id, title, abs, url); } } 这个函数通过应用程序上下文的ContentResolver接口resolver的query函数来获得与Articles.CONTENT_POS_URI这个URI对应的文章信息条目。常量Articles.CONTENT_POS_URI是在应用程序ArticlesProvider中定义的,它的值为“content://shy.luo.providers.articles/pos”,通过调用ContentUris.withA

19、ppendedId函数来在后面添加了一个整数,表示要获取指定位置的文章信息条目。这个位置是指ArticlesProvider这个Content Provider中的所有文章信息条目按照Articles.DEFAULT_SORT_ORDER来排序后得到的位置的,常量Articles.DEFAULT_SORT_ORDER也是在应用程序ArticlesProvider中定义的,它的值为“_id asc”,即按照文章信息的ID值从小到大来排列。 Step 2. ContentResolver.query 这个函数定义在frameworks/base/core/java/

20、android/content/ContentResolver.java文件中: [java] view plain copy 在CODE上查看代码片派生到我的代码片 public abstract class ContentResolver { ...... public final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { I

21、ContentProvider provider = acquireProvider(uri); if (provider == null) { return null; } try { ...... Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder); ......

22、 return new CursorWrapperInner(qCursor, provider); } catch (RemoteException e) { ...... } catch(RuntimeException e) { ...... } } ...... } 这个函数首先通过调用acquireProvider函数来获得与参数uri对应的Content Prov

23、ider接口,然后再通过这个接口的query函数来获得相应的数据。我们先来看看acquireProvider函数的实现,再回过头来分析这个Content Provider接口的query函数的实现。 Step 3. ContentResolver.acquireProvider Step 4. ApplicationContentResolver.acquireProvider Step 5. ActivityThread.acquireProvider Step 6. ActivityThread.getProvider

24、 从Step 3到Step 6是获取与上面Step 2中传进来的参数uri对应的Content Provider接口的过程。在前面一篇文章 中,我们已经详细介绍过这个过程了,这里不再详述。不过这里我们假设,这个Content Provider接口之前已经创建好了,因此,在Step 6的ActivityThread.getProvider函数中,通过getExistingProvider函数就把之前已经好的Content Provider接口返回来了。 回到Step 2中的ContentResolver.query函数中,它继续调用这个返回来的Content

25、 Provider接口来获取数据。从这篇文章 中,我们知道,这个Content Provider接口实际上是一个在ContentProvider类的内部所创建的一个Transport对象的远程接口。这个Transport类继承了ContentProviderNative类,是一个Binder对象的Stub类,因此,接下来就会进入到这个Binder对象的Proxy类ContentProviderProxy中执行query函数。 Step 7. ContentProviderProxy.query 这个函数定义在frameworks/base/core/j

26、ava/android/content/ContentProviderNative.java文件中: [java] view plain copy 在CODE上查看代码片派生到我的代码片 final class ContentProviderProxy implements IContentProvider { ...... public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, Strin

27、g 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(); IBulkC

28、ursor bulkCursor = bulkQueryInternal( url, projection, selection, selectionArgs, sortOrder, adaptor.getObserver(), window, adaptor); if (bulkCursor == null) { return null; } return adaptor; }

29、 ...... } 这个函数首先会创建一个CursorWindow对象,前面已经说过,这个CursorWindow对象包含了一块匿名共享内存,它的作用是把这块匿名共享内存通过Binder进程间通信机制传给Content Proivder,好让Content Proivder在里面返回所请求的数据。下面我们就先看看这个CursorWindow对象的创建过程,重点关注它是如何在内部创建匿名共享内存的,然后再回过头来看看它调用bulkQueryInternal函数来做了些什么事情。 CursorWindow类定义在frameworks/base/

30、core/java/android/database/CursorWindow.java文件中,我们来看看它的构造函数的实现: [java] view plain copy 在CODE上查看代码片派生到我的代码片 public class CursorWindow extends SQLiteClosable implements Parcelable { ...... private int nWindow; ...... public CursorWindow(boolean localWindow)

31、{ ...... native_init(localWindow); } ...... } 它主要调用本地方法native_init来执行初始化的工作,主要就是创建匿名共享内存了,传进来的参数localWindow为false,表示这个匿名共享内存只能通过远程调用来访问,即前面我们所说的,通过Content Proivder返回来的Cursor接口来访问这块匿名共享内存里面的数据。 Step 8. CursorWindow.native_init

32、 这是一个JNI方法,它对应定义在frameworks/base/core/jni/android_database_CursorWindow.cpp文件中的native_init_empty函数: [cpp] view plain copy 在CODE上查看代码片派生到我的代码片 static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"native_init", "(Z)V", (void *)native_init_empty}, ...

33、 }; 函数native_init_empty的定义如下所示: [cpp] view plain copy 在CODE上查看代码片派生到我的代码片 static void native_init_empty(JNIEnv * env, jobject object, jboolean localOnly) { ...... CursorWindow * window; window = new CursorWindow(MAX_WINDOW_SIZE); ......

34、 if (!window->initBuffer(localOnly)) { ...... } ...... SET_WINDOW(env, object, window); } 这个函数在C++层创建了一个CursorWindow对象,然后通过调用SET_WINDOW宏来把这个C++层的CursorWindow对象与Java层的CursorWindow对象关系起来: [cpp] view plain copy 在CODE上查看代码片派生到我的代码片 #define SET_W

35、INDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window)) 这里的gWindowField即定义为Java层的CursorWindow对象中的nWindow成员变量: [cpp] view plain copy 在CODE上查看代码片派生到我的代码片 static jfieldID gWindowField; ...... int register_android_database_CursorWindow(JNIEnv * env)

36、 { jclass clazz; clazz = env->FindClass("android/database/CursorWindow"); ...... gWindowField = env->GetFieldID(clazz, "nWindow", "I"); ...... } 这种用法在Android应用程序框架层中非常普遍。 下面我们重点关注C++层的CursorWindow对象的initBuffer函数的实现。 St

37、ep 9. CursorWindow.initBuffer C++层的CursorWindow类定义在frameworks/base/core/jni/CursorWindow.cpp文件中: [cpp] view plain copy 在CODE上查看代码片派生到我的代码片 bool CursorWindow::initBuffer(bool localOnly) { ...... sp heap; heap = new MemoryHeapBase(mMaxSize, 0,

38、 "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 =

39、 mMaxSize; ...... } } ...... } else { ...... } } 这里我们就可以很清楚地看到,在CursorWindow类的内部有一个成员变量mMemory,它的类型是MemoryBase。MemoryBase类为我们封装了匿名共享内存的访问以及在进程间的传输等问题,具体可以参考前面一篇文章 ,这里就不再详述了。 通过Step 8和Step 9

40、两步,用来在第三方应用程序和Content Provider之间传输数据的媒介就准备好了,我们回到Step 7中,看看系统是如何把这个匿名共享存传递给Content Provider使用的。在Step 7中,最后调用bulkQueryInternal函数来进一步操作。 Step 10. ContentProviderProxy.bulkQueryInternal 这个函数定义在frameworks/base/core/java/android/content/ContentProviderNative.java文件中: [java] view plai

41、n copy 在CODE上查看代码片派生到我的代码片 final class ContentProviderProxy implements IContentProvider { ...... private IBulkCursor bulkQueryInternal( Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder, IConten

42、tObserver observer, CursorWindow window, BulkCursorToCursorAdaptor adaptor) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); url.writeToPar

43、cel(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.writeStri

44、ng(selection); if (selectionArgs != null) { length = selectionArgs.length; } else { length = 0; } data.writeInt(length); for (int i = 0; i < length; i++) { data.writeString(selectionArgs[i]);

45、 } 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

46、 // 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.rea

47、dExceptionFromParcel(reply); IBulkCursor bulkCursor = null; IBinder bulkCursorBinder = reply.readStrongBinder(); if (bulkCursorBinder != null) { bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder); if (wantsCursorMetadata

48、) { int rowCount = reply.readInt(); int idColumnPosition = reply.readInt(); if (bulkCursor != null) { adaptor.set(bulkCursor, rowCount, idColumnPosition); } } }

49、 data.recycle(); reply.recycle(); return bulkCursor; } ...... } 这个函数有点长,不过它的逻辑很简单,就是把查询参数都写到一个Parcel对象data中去,然后通过下面Binder进程间通信机制把查询请求传给Content Provider处理: [java] view plain copy 在CODE上查看代码片派生到我的代码片 mRemote.transact(IContentProvider.QUERY_TRAN

50、SACTION, data, reply, 0); 从这个Binder调用返回以后,就会得到一个IBulkCursor接口,它是一个Binder引用,实际是指向在Content Provider这一侧创建的一个CursorToBulkCursorAdaptor对象,后面我们将会看到。有了这个IBulkCursor接口之后,我们就可以通过Binder进程间调用来访问从Content Provider中查询得到的数据了。这个IBulkCursor接口最终最设置了上面Step 7中创建的BulkCursorToCursorAdaptor对象adaptor中去: [java] vi

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服