第二个关键点是在MediaProvider客户端示例中所调用的MediaStore.Image.Media 的query函数。MediaStore是多媒体开发中常用的类,其内部定义了专门针对Image、Audio、Video等不同多媒体信息的内部类来帮助客户端开发人员更好底和MediaProvider交互。这些类及相互之间的关系如图7-1所示。
:-: ![](http://img.blog.csdn.net/20150803130759625?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图7-1 MediaStore类图
由图7-1可知,MediaStore定义了较多的内部类,我们重点展示作为内部类之一的Image的情况,其中:
- MediaColumns定义了所有与媒体相关的数据库表都会用到的数据库字段,而ImageColumns定义了单独针对Image的数据库字段。
- Image类定义了一个名为Media的内部类用于查询和Image相关的信息,同时Image类还定义了一个名为Thumbnails的内部类用于查询和Image相关的缩略图的信息(在Android平台上,缩略图的来源有两种,一种是Image,另一种是Video,故Image定义了名为Thumbnails的内部类,而Video也定义了一个名为Thumbnails的内部类)。
* * * * *
**提示**:MediaStore类较为复杂,主要原因是它定义了一些同名类。读者阅读代码时务须仔细。
* * * * *
下面来看Image.Media的query函数,其代码非常简单,如下所示:
**MediaStore.java::Image.Media.query**
~~~
public static final class Media implementsImageColumns {
public static final Cursor query(ContentResolvercr,Uri uri,
String[]projection) {
//直接调用ContentResolver的query函数
returncr.query(uri, projection, null,
null,DEFAULT_SORT_ORDER);
}
~~~
Image.Media的query函数直接调用ContentResolver的query函数,虽然cr的真实类型是ApplicationContentResolver,但是此函数却由其基类ContentResolver实现。
* * * * *
**提示**:追求运行效率的程序员也许会对上边这段代码的实现略有微词,因为Image.Media的query函数基本上没做任何有意义的工作。假如客户端直接调用cr.query函数,则此处的query就增加了一次函数调用和返回的开销(即Image.Media query调用和返回时参数的入栈/出栈)。但是,通过Image.Media的封装将使程序更清晰和易读(与直接使用ContentResolver的query相比,代码阅读者一看Image.Media就知道其query函数应该和Image有关,否则需要通过解析uri参数才能确定查询的信息是什么)。代码清晰易读和运行效率高,往往是软件开发中的熊掌和鱼,它们之间的对立性,将在本章中体现得淋漓尽致。笔者建议读者在实际开发中结合具体情况决定取舍,万不可钻牛角尖。
* * * * *
1. ContentResolver的query函数分析
**ContentResolver.java::query**
~~~
public final Cursor query(Uri uri, String[]projection,
String selection, String[] selectionArgs, String sortOrder) {
//调用acquireProvider函数,参数为uri,函数也由ContentResolver实现
IContentProvider provider = acquireProvider(uri);
//注意:下面将和ContentProvider交互, 相关知识留待7.4节再分析
......
}
~~~
ContentResolver的query将调用acquireProvider,该函数定义在ContentResolver类中,代码如下:
**ContentResolver.java::query**
~~~
public final IContentProvider acquireProvider(Uriuri) {
if(!SCHEME_CONTENT.equals(uri.getScheme())) return null;
Stringauth = uri.getAuthority();
if (auth!= null) {
//acquireProvider是一个抽象函数,由ContentResolver的子类实现。在本例中,该函数
//将由ApplicationContentResolver实现。uri.getAuthority将返回代表目标
//ContentProvider的名字
returnacquireProvider(mContext, uri.getAuthority());
}
returnnull;
}
~~~
如上所述,acquireProvider由ContentResolver的子类实现,在本例中该函数由ApplicationContentResolver定义,代码如下:
**ContextImpl.java::acquireProvider**
~~~
protected IContentProvider acquireProvider(Contextcontext, String name) {
//mMainThread指向代表应用进程主线程的ActivityThread对象,每个应用进程只有一个
//ActivityThread对象
return mMainThread.acquireProvider(context,name);
}
~~~
如以上代码所示,最终ActivityThread的acquireProvider函数将被调用,希望它不要再被层层转包了。
2. AcitvityThread的acquireProvider函数分析
ActivityThread的 acquireProvider函数的代码如下:
**ActivityThread.java::acquireProvider**
~~~
public final IContentProvideracquireProvider(Context c, String name) {
//①调用getProvider函数,它很重要。见下文分析
IContentProvider provider = getProvider(c,name);
......
IBinderjBinder = provider.asBinder();
synchronized(mProviderMap) {
//客户端进程将本进程使用的ContentProvider信息保存到mProviderRefCountMap中,
//其主要功能与引用计数和资源释放有关,读者暂可不理会它
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if(prc== null)
mProviderRefCountMap.put(jBinder, newProviderRefCount(1));
else prc.count++;
}
returnprovider;
}
~~~
在acquireProvider内部调用getProvider得到一个IContentProvider类型的对象,该函数非常重要,其代码为:
**ActivityThread.java::getProvider**
~~~
private IContentProvider getProvider(Contextcontext, String name) {
/*
查询该应用进程是否已经保存了用于和远端ContentProvider通信的对象existing。
此处,我们知道existing的类型是IContentProvider,不过IContentProvider是一
个interface,那么existing的真实类型是什么呢?稍后再揭示
*/
IContentProvider existing = getExistingProvider(context, name);
if(existing != null) return existing;//如果existing存在,则直接返回
IActivityManager.ContentProviderHolder holder = null;
try {
//如果existing不存在,则需要向AMS查询,返回值的类型为ContentProviderHolder
holder= ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), name);
}......
//注意:记住下面这个函数调用,此时是在客户端进程中
IContentProvider prov = installProvider(context, holder.provider,
holder.info, true);
......
returnprov;
}
~~~
以上代码中让人比较头疼的是其中新出现的几种数据类型,如IContentProvider、ContentProviderHolder。先来分析AMS的getContentProvider。
3. AMS的getContentProvider函数分析
getContentProvider的功能主要由getContentProviderImpl函数实现,故此处可直接对它进行分析。
(1) getContentProviderImpl启动目标进程
getContentProviderImpl函数较长,可分段来看,先来分析下面一段代码。
**ActivityManagerService.java::getContentProviderImpl**
~~~
privatefinal ContentProviderHolder getContentProviderImpl(
IApplicationThread caller, String name) {
ContentProviderRecord cpr;
ProviderInfo cpi = null;
synchronized(this) {
ProcessRecord r = null;
if(caller != null) {
r= getRecordForAppLocked(caller);
if (r == null)......//如果查询不到调用者信息,则抛出SecurityException
}// if (caller != null)判断结束
//name参数为调用进程指定的代表目标ContentProvider的authority
cpr =mProvidersByName.get(name);
//如果cpr不为空,表明该ContentProvider已经在AMS中注册
booleanproviderRunning = cpr != null;
if(providerRunning){
......//如果该ContentProvider已经存在,则进行对应处理, 相关内容可自行阅读
}
//如果目标ContentProvider对应进程还未启动
if(!providerRunning) {
try {
//查询PKMS,得到指定的ProviderInfo信息
cpi = AppGlobals.getPackageManager().resolveContentProvider(
name,STOCK_PM_FLAGS |
PackageManager.GET_URI_PERMISSION_PATTERNS);
}......
String msg;
//权限检查,此处不作讨论
if((msg=checkContentProviderPermissionLocked(cpi, r)) != null)
throw new SecurityException(msg);
/*
如果system_server还没启动完毕,并且该ContentProvider不运行在system_server
中,则此时不允许启动ContentProvider。读者还记得哪个ContentProvider运行在
system_server进程中吗?答案是SettingsProvider
*/
.......
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
cpr =mProvidersByClass.get(comp);
finalboolean firstClass = cpr == null;
//初次启动MediaProvider对应进程时,firstClass一定为true
if (firstClass) {
try {
//查询PKMS,得到MediaProvider所在的Application信息
ApplicationInfoai =
AppGlobals.getPackageManager().getApplicationInfo(
cpi.applicationInfo.packageName, STOCK_PM_FLAGS);
if (ai == null) return null;
//在AMS内部通过ContentProviderRecord来保存ContentProvider的信息,类似
//ActivityRecord,BroadcastRecord等
cpr = new ContentProviderRecord(cpi, ai,comp);
}......
}// if(firstClass)判断结束
~~~
以上代码的逻辑比较简单,主要是为目标ContentProvider(即MediaProvider)创建一个ContentProviderRecord对象。结合第6章的知识,AMS为四大组件都设计了对应的数据结构,如ActivityRecord、BroadcastRecord等。
接着看getContentProviderImpl,其下一步的工作就是启动目标进程:
**ActivityManagerService.java::getContentProviderImpl**
~~~
/*
canRunHere函数用于判断目标CP能否运行在r所对应的进程(即调用者所在进程)
该函数内部做如下判断:
(info.multiprocess|| info.processName.equals(app.processName))
&& (uid == Process.SYSTEM_UID || uid == app.info.uid)
就本例而言,MediaProvider不能运行在客户端进程中
*/
if (r !=null && cpr.canRunHere(r)) returncpr;
finalint N = mLaunchingProviders.size();
......//查找目标ContentProvider对应的进程是否正处于启动状态
//如果i大于等于N,表明目标进程的信息不在mLaunchingProviders中
if (i>= N) {
finallong origId = Binder.clearCallingIdentity();
......
//调用startProcessLocked函数创建目标进程
ProcessRecord proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0,"content provider",
newComponentName(cpi.applicationInfo.packageName,
cpi.name), false);
if(proc == null)return null;
cpr.launchingApp = proc;
//将该进程信息保存到mLaunchingProviders中
mLaunchingProviders.add(cpr);
}
if(firstClass) mProvidersByClass.put(comp, cpr);
mProvidersByName.put(name, cpr);
/*
下面这个函数将为客户端进程和目标CP进程建立紧密的关系,即当目标CP进程死亡后,
AMS将根据该函数建立的关系找到客户端进程并杀死(kill)它们。在7.2.3节
有对这个函数的相关解释
*/
incProviderCount(r, cpr);
if(cpr.launchingApp == null) return null;
try {
cpr.wait();//等待目前进程的启动
} ......
}// synchronized(this)结束
returncpr;
}
~~~
通过对以上代码的分析发现,getContentProviderImpl将等待一个事件,想必读者也能明白,此处一定是在等待目标进程启动并创建好MediaProvider。目标进程的这部分工作用专业词语来表达就是发布目标ContentProvider(即本例的MediaProvider)。
(2) MediaProvider的创建
根据第6章的介绍,目标进程启动后要做的第一件大事就是调用AMS的attachApplication函数,该函数的主要功能由attachApplicationLocked完成。我们回顾一下相关代码。
**ActivityManagerService.java::attachApplicationLocked**
~~~
private final booleanattachApplicationLocked(IApplicationThread thread,
int pid) {
......
//通过PKMS查询运行在该进程中的CP信息,并保存到mProvidersByClass中
Listproviders = normalMode ?
generateApplicationProvidersLocked(app) : null;
//调用目标应用进程的bindApplication函数,此处将providers信息传递给目标进程
thread.bindApplication(processName,appInfo, providers,
app.instrumentationClass, profileFile,
......);
......
}
~~~
再来看目标进程bindApplication的实现,其内部最终会通过handleBindApplication函数处理,我们回顾一下相关代码。
**ActivtyThread.java::handleBindApplication**
~~~
private void handleBindApplication(AppBindDatadata) {
......
if(!data.restrictedBackupMode){
List<ProviderInfo> providers = data.providers;
if(providers != null) {
//调用installContentProviders安装
installContentProviders(app, providers);
......
}
}
......
}
~~~
AMS传递过来的ProviderInfo列表将由目标进程的installContentProviders处理,其相关代码如下:
**ActivtyThread.java::installContentProviders**
~~~
private void installContentProviders(Contextcontext,
List<ProviderInfo>providers) {
finalArrayList<IActivityManager.ContentProviderHolder> results =
newArrayList<IActivityManager.ContentProviderHolder>();
Iterator<ProviderInfo> i = providers.iterator();
while(i.hasNext()) {
//①也调用installProvider,注意该函数传递的第二个参数为null
IContentProvider cp = installProvider(context, null, cpi, false);
if (cp!= null) {
IActivityManager.ContentProviderHolder cph =
newIActivityManager.ContentProviderHolder(cpi);
cph.provider = cp;
results.add(cph);//将信息添加到results数组中
......//创建引用计数
}
}//while循环结束
try { //②调用AMS的publishContentProviders发布ContentProviders
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} ......
}
~~~
以上代码列出了两个关键点,分别是:
- 调用installProvider得到一个IContentProvider类型的对象。
- 调用AMS的publishContentProviders发布本进程所运行的ContentProvider。该函数留到后面再作分析
在继续分析之前,笔者要特别强调installProvider,该函数既在客户端进程中被调用(还记得7.2.2节ActivityThread的acquireProvider函数中那句注释吗?),又在目标进程(即此处MediaProvider所在进程)中被调用。与客户端进程的调用相比,只在一处有明显的不同:
- 客户端进程调用installProvider函数时,该函数的第二个参数不为null。
- 目标进程调用installProvider函数时,该函数的第二个参数硬编码为null。
我们曾经在6.2.3分析过installProvider函数,结合那里的介绍可知:installProvider是一个通用函数,不论客户端使用远端的CP还是目标进程安装运行在其上的CP上,最终都会调用它,只不过参数不同罢了。
来看installProvider函数,其代码如下:
**ActivityThread.java::installProvider**
~~~
private IContentProvider installProvider(Contextcontext,
IContentProvider provider, ProviderInfo info, boolean noisy) {
ContentProvider localProvider = null;
if(provider == null) {//针对目标进程的情况
Context c = null;
ApplicationInfo ai = info.applicationInfo;
if(context.getPackageName().equals(ai.packageName)) {
c = context;
}......//这部分代码已经在6.2.3节分析过了,其目的就是为了得到正确的
//Context用于加载Java字节码
try{
final java.lang.ClassLoader cl = c.getClassLoader();
//通过Java反射机制创建MediaProvider实例
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
//注意下面这句代码
provider = localProvider.getIContentProvider();
}
} elseif (localLOGV) {
Slog.v(TAG, "Installing external provider " + info.authority +": "
+ info.name);
}// if(provider == null)判断结束
/*
由以上代码可知,对于provider不为null 的情况(即客户端调用的情况),该函数没有
什么特殊的处理
*/
......
/*
引用计数及设置DeathReceipient等相关操作。在6.2.3节的第2个小标题中
曾说过,目标进程为自己进程中的CP实例设置DeathReceipient没有作用,因为二者在同一个
进程中,自己怎么能接收自己的讣告消息呢?不过,如果客户端进程为目标进程的CP设置
DeathReceipient又有作用吗?仔细思考这个问题
*/
returnprovider;//最终返回的对象是IContentProvider类型,它到底是什么呢?
}
~~~
由以代码可知,installProvider最终返回的是一个IContentProvider类型的对象。对于目标进程而言,该对象是通过调用CP的实例对象的(本例就是MediaProvider) getIContentProvider函数得来的。而对于客户端进程而言,该对象是由installProvider第二个参数传递进来的,那么,这个IContentProvider到底是什么?
(3) IContentProvider的真面目
要说清楚IContentProvider,就先来看ContentProvider家族的类图,如图7-2所示。
:-: ![](http://img.blog.csdn.net/20150803130820677?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图7-2 ContentProvider类图
图7-2揭示了IContentProvider的真面目,具体介绍如下:
- 每个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函数)。
关于目标进程这一系列的调用函数,不妨先看看Transport的query函数,其代码为:
**ContentProvider.java::Transport.query**
~~~
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
enforceReadPermission(uri);
//Transport为ContentProvider的内部类,此处将调用ContentProvider的query函数
//本例中,该query函数由MediaProvider实现,故最终会调用MediaProvider的query
returnContentProvider.this.query(uri, projection, selection,
selectionArgs, sortOrder);
}
~~~
务必弄清楚,此处只有一个目标ContentProvider的实例,即只有一个MediaProvider对象。Transport的 query函数内部虽然调用的是基类ContentProvider的query函数,但是根据面向对象的多态原理,该函数最终由其子类(本例中是MediaProvider)来实现。
认识了IContentProvider,即知道了客户端进程和目标进程的交互接口。
继续我们的分析。此时目标进程需要将MediaProvider的信息通过AMS发布出去。
(4) AMS pulishContentProviders分析
要把目标进程的CP信息发布出去,需借助AMS 的pulishContentProviders函数,其代码如下:
**ActivityManagerService.java::publishContentProviders**
~~~
public final voidpublishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
......
synchronized(this) {
finalProcessRecord r = getRecordForAppLocked(caller);
finallong origId = Binder.clearCallingIdentity();
final int N = providers.size();
for (int i=0; i<N; i++) {
ContentProviderHolder src = providers.get(i);
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if(dst != null) {
......//将相关信息分别保存到mProviderByClass和mProvidersByName中
int NL = mLaunchingProviders.size();
...... //目标进程已经启动,将其从mLaunchingProviders移除
synchronized (dst) {
dst.provider = src.provider;//将信息保存在dst中
dst.proc = r;
//触发还等在(wait)getContentProvider中的那个客户端进程
dst.notifyAll();
}
updateOomAdjLocked(r);//调节目标进程的oom_adj等相关参数
}// if(dst != null)判断结束
......
}
}
~~~
至此,客户端进程将从getContentProvider中返回,并调用installProvider函数。根据前面的分析,客户端进程调用installProvider时,其第二个参数不为null,即客户端进程已经从AMS中得到了能直接和目标进程交互的IContentProvider Bp端对象。此后,客户端就可直接使用该对象向目标进程发起请求。
- 前言
- 第1章 搭建Android源码工作环境
- 1.1 Android系统架构
- 1.2 搭建开发环境
- 1.2.1 下载源码
- 1.2.2 编译源码
- 1.2.3 利用Eclipse调试system_process
- 1.3 本章小结
- 第2章 深入理解Java Binder和MessageQueue
- 2.1 概述
- 2.2 Java层中的Binder架构分析
- 2.2.1 Binder架构总览
- 2.2.2 初始化Java层Binder框架
- 2.2.3 addService实例分析
- 2.2.4 Java层Binder架构总结
- 2.3 心系两界的MessageQueue
- 2.3.1 MessageQueue的创建
- 2.3.2 提取消息
- 2.3.3 nativePollOnce函数分析
- 2.3.4 MessageQueue总结
- 2.4 本章小结
- 第3章 深入理解SystemServer
- 3.1 概述
- 3.2 SystemServer分析
- 3.2.1 main函数分析
- 3.2.2 Service群英会
- 3.3 EntropyService分析
- 3.4 DropBoxManagerService分析
- 3.4.1 DBMS构造函数分析
- 3.4.2 dropbox日志文件的添加
- 3.4.3 DBMS和settings数据库
- 3.5 DiskStatsService和DeviceStorageMonitorService分析
- 3.5.1 DiskStatsService分析
- 3.5.2 DeviceStorageManagerService分析
- 3.6 SamplingProfilerService分析
- 3.6.1 SamplingProfilerService构造函数分析
- 3.6.2 SamplingProfilerIntegration分析
- 3.7 ClipboardService分析
- 3.7.1 复制数据到剪贴板
- 3.7.2 从剪切板粘贴数据
- 3.7.3 CBS中的权限管理
- 3.8 本章小结
- 第4章 深入理解PackageManagerService
- 4.1 概述
- 4.2 初识PackageManagerService
- 4.3 PKMS的main函数分析
- 4.3.1 构造函数分析之前期准备工作
- 4.3.2 构造函数分析之扫描Package
- 4.3.3 构造函数分析之扫尾工作
- 4.3.4 PKMS构造函数总结
- 4.4 APK Installation分析
- 4.4.1 adb install分析
- 4.4.2 pm分析
- 4.4.3 installPackageWithVerification函数分析
- 4.4.4 APK 安装流程总结
- 4.4.5 Verification介绍
- 4.5 queryIntentActivities分析
- 4.5.1 Intent及IntentFilter介绍
- 4.5.2 Activity信息的管理
- 4.5.3 Intent 匹配查询分析
- 4.5.4 queryIntentActivities总结
- 4.6 installd及UserManager介绍
- 4.6.1 installd介绍
- 4.6.2 UserManager介绍
- 4.7 本章学习指导
- 4.8 本章小结
- 第5章 深入理解PowerManagerService
- 5.1 概述
- 5.2 初识PowerManagerService
- 5.2.1 PMS构造函数分析
- 5.2.2 init分析
- 5.2.3 systemReady分析
- 5.2.4 BootComplete处理
- 5.2.5 初识PowerManagerService总结
- 5.3 PMS WakeLock分析
- 5.3.1 WakeLock客户端分析
- 5.3.2 PMS acquireWakeLock分析
- 5.3.3 Power类及LightService类介绍
- 5.3.4 WakeLock总结
- 5.4 userActivity及Power按键处理分析
- 5.4.1 userActivity分析
- 5.4.2 Power按键处理分析
- 5.5 BatteryService及BatteryStatsService分析
- 5.5.1 BatteryService分析
- 5.5.2 BatteryStatsService分析
- 5.5.3 BatteryService及BatteryStatsService总结
- 5.6 本章学习指导
- 5.7 本章小结
- 第6章 深入理解ActivityManagerService
- 6.1 概述
- 6.2 初识ActivityManagerService
- 6.2.1 ActivityManagerService的main函数分析
- 6.2.2 AMS的 setSystemProcess分析
- 6.2.3 AMS的 installSystemProviders函数分析
- 6.2.4 AMS的 systemReady分析
- 6.2.5 初识ActivityManagerService总结
- 6.3 startActivity分析
- 6.3.1 从am说起
- 6.3.2 AMS的startActivityAndWait函数分析
- 6.3.3 startActivityLocked分析
- 6.4 Broadcast和BroadcastReceiver分析
- 6.4.1 registerReceiver流程分析
- 6.4.2 sendBroadcast流程分析
- 6.4.3 BROADCAST_INTENT_MSG消息处理函数
- 6.4.4 应用进程处理广播分析
- 6.4.5 广播处理总结
- 6.5 startService之按图索骥
- 6.5.1 Service知识介绍
- 6.5.2 startService流程图
- 6.6 AMS中的进程管理
- 6.6.1 Linux进程管理介绍
- 6.6.2 关于Android中的进程管理的介绍
- 6.6.3 AMS进程管理函数分析
- 6.6.4 AMS进程管理总结
- 6.7 App的 Crash处理
- 6.7.1 应用进程的Crash处理
- 6.7.2 AMS的handleApplicationCrash分析
- 6.7.3 AppDeathRecipient binderDied分析
- 6.7.4 App的Crash处理总结
- 6.8 本章学习指导
- 6.9 本章小结
- 第7章 深入理解ContentProvider
- 7.1 概述
- 7.2 MediaProvider的启动及创建
- 7.2.1 Context的getContentResolver函数分析
- 7.2.2 MediaStore.Image.Media的query函数分析
- 7.2.3 MediaProvider的启动及创建总结
- 7.3 SQLite创建数据库分析
- 7.3.1 SQLite及SQLiteDatabase家族
- 7.3.2 MediaProvider创建数据库分析
- 7.3.3 SQLiteDatabase创建数据库的分析总结
- 7.4 Cursor 的query函数的实现分析
- 7.4.1 提取query关键点
- 7.4.2 MediaProvider 的query分析
- 7.4.3 query关键点分析
- 7.4.4 Cursor query实现分析总结
- 7.5 Cursor close函数实现分析
- 7.5.1 客户端close的分析
- 7.5.2 服务端close的分析
- 7.5.3 finalize函数分析
- 7.5.4 Cursor close函数总结
- 7.6 ContentResolver openAssetFileDescriptor函数分析
- 7.6.1 openAssetFileDescriptor之客户端调用分析
- 7.6.2 ContentProvider的 openTypedAssetFile函数分析
- 7.6.3 跨进程传递文件描述符的探讨
- 7.6.4 openAssetFileDescriptor函数分析总结
- 7.7 本章学习指导
- 7.8 本章小结
- 第8章 深入理解ContentService和AccountManagerService
- 8.1 概述
- 8.2 数据更新通知机制分析
- 8.2.1 初识ContentService
- 8.2.2 ContentResovler 的registerContentObserver分析
- 8.2.3 ContentResolver的 notifyChange分析
- 8.2.4 数据更新通知机制总结和深入探讨
- 8.3 AccountManagerService分析
- 8.3.1 初识AccountManagerService
- 8.3.2 AccountManager addAccount分析
- 8.3.3 AccountManagerService的分析总结
- 8.4 数据同步管理SyncManager分析
- 8.4.1 初识SyncManager
- 8.4.2 ContentResolver 的requestSync分析
- 8.4.3 数据同步管理SyncManager分析总结
- 8.5 本章学习指导
- 8.6 本章小结