跟着Innost理解下ContentProvider

重点分析ContentProvider、SQLite、Cursor query、close函数的实现及ContentResolver openAssetFileDescriptor函数。四条分析路线。
· 第一条:以客户端进程通过query来查询相关信息为入口点,分析系统如何创建和启动ContentProvider。此分析路线着重关注客户端进程、ActivityManagerService及MediaProvider所在进程间的交互。
· 第二条:沿袭第一条分析路径,但是将关注焦点转移到SQLiteDatabase如何创建数据库的分析上。另外,本条路线还将对SQLite进行相关介绍。
· 第三条:将重点研究Cursor query和close函数的实现细节。
· 第四条:将分析ContentResolver openAssetFileDescriptor函数的实现。

1. ContentProvider的启动及创建

第一、二、三条分析路线都将以下面这段示例为参考。

void QueryImage(Context context){

  //①得到ContentResolver对象

 ContentResolver cr = context.getContentResover();

  Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

  //②查询数据库

  Cursorcursor = MediaStore.Images.Media.query(cr,uri,null);

 cursor.moveToFirst();//③移动游标到头部

  ......//从游标中取出数据集

  cursor.close();//④关闭游标

}

客户端(即运行本示例的进程)查询(query)的目标ContentProvider是MediaProvider,它运行于进程android.process.media中。假设目标进程此时还未启动。
本节的关注点集中在:
· MediaProvider所在进程是如何创建的?MediaProvider又是如何创建的?
· 客户端通过什么和位于目标进程中的MediaProvider交互的?
这里写图片描述

1.1 Context的getContentResolver函数分析

Context的getContentResolver最终会调用它所代理的ContextImpl对象的getContentResolver函数. 该函数直接返回mContentResolver,此变量在ContextImpl初始化时创建. mContentResolver的真实类型是ApplicationContentResolver,它是ContextImpl定义的内部类并继承了ContentResolver。

1.2 ContentResolver.query函数分析

ContentResolver的query将调用acquireProvider,该函数定义在ContentResolver类中. acquireProvider由ContentResolver的子类实现,在本例中该函数由ApplicationContentResolver定义。
ApplicationContentResolver.acquireProvider()将调用AcitvityThread.acquireProvider();

1.3 AcitvityThread.acquireProvider()

通过调用getProvider()取得IContentProvider,然后将这个ContentProvider 保存到客户端进程的mProviderRefCountMap;

1.3.1 getProvider()
查询该应用进程是否已经保存了用于和远端ContentProvider通信的对象。如果存在,则直接返回;如果不存在,则调用AMS.getContentProvider()获取ContentProviderHolder,然后在当前应用进程调用installProvider()安装。

1.4 AMS.getContentProvider()

getContentProvider的功能主要由getContentProviderImpl函数实现,故此处可直接对它进行分析。

1.4.1 getContentProviderImpl启动目标进程
为目标ContentProvider(即MediaProvider)创建一个ContentProviderRecord对象。
如果目标进程没有启动,则启动之。然后AMS等待目标进程发布相应的目标ContentProvider。

1.4.2 目标进程创建目标ContentProvider
目标进程启动后要做的第一件大事就是调用AMS的attachApplication函数,该函数的主要功能由attachApplicationLocked完成。
booleanattachApplicationLocked()通过PKMS查询运行在该进程中的CP信息,并保存到mProvidersByClass中;调用目标应用进程的bindApplication函数,此处将providers信息传递给目标进程。
再来看目标进程bindApplication的实现,其内部最终会通过handleBindApplication函数处理。
AMS传递过来的ProviderInfo列表将由目标进程的installContentProviders处理。

1.4.3 installProvider()
installProvider是一个通用函数,不论客户端使用远端的CP还是目标进程安装运行在其上的CP上,最终都会调用它,只不过参数不同罢了:
· 客户端进程调用installProvider函数时,该函数的第二个参数不为null。
· 目标进程调用installProvider函数时,该函数的第二个参数硬编码为null。

1.4.4 IContentProvider Hierachy
· 每个ContentProvider实例中都有一个mTransport成员,其类型为Transport。
· Transport类从ContentProviderNative派生。由图7-2可知,ContentProviderNative从Binder类派生,并实现了IContentProvider接口。结合前面的代码,IContentProvider将是客户端进程和目标进程交互的接口,即目标进程使用IContentProvider的Bn端Transport,而客户端使用IContentProvider的Bp端,其类型是ContentProviderProxy(定义在ContentProviderNative.java中)。

这里写图片描述

客户端如何通过IContentProvider query函数和目标CP进程交互的呢?其流程如下:
· CP客户端得到IContentProvider的Bp端(实际类型是ContentProviderProxy),并调用其query函数,在该函数内部将参数信息打包,传递给Transport(它是IContentProvider的Bn端)。
· Transport的onTransact函数将调用Transport的query函数,而Transport的query函数又将调用ContentProvider子类定义的query函数(即MediaProvider的query函数)。

1.4.5 AMS.pulishContentProviders()
要把目标进程的CP信息发布出去,需借助AMS 的pulishContentProviders函数。

1.5 客户端进程和目标CP紧密的关系

· 该关系的建立是在AMS getContentProviderImpl函数中调用incProviderCount完成的,关系的确立以ContentProviderRecorder保存客户端进程的ProcessRecord信息为标识。
· 一旦CP进程死亡,AMS能根据该ContentProviderRecorder中保存的客户端信息找到使用该CP的所有客户端进程,然后再杀死它们。

客户端能否撤销这种紧密关系呢?答案是肯定的,但这和Curso

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值