第二個關鍵點是在MediaProvider客戶端示例中所調用的MediaStore.Image.Media 的query函數。MediaStore是多媒體開發中常用的類,其內部定義了專門針對Image、Audio、Video等不同多媒體信息的內部類來幫助客戶端開發人員更好底和MediaProvider交互。這些類及相互之間的關系如圖7-1所示。
:-: 
圖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所示。
:-: 
圖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 本章小結