重点分析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