·??深入分析ContentProvider的創(chuàng)建和啟動(dòng),以及SQLite相關(guān)的知識(shí)點(diǎn)
·??深入分析Cursor query和close函數(shù)的實(shí)現(xiàn)
·??深入分析ContentResolver openAssetFileDescriptor函數(shù)的實(shí)現(xiàn)
·??ActivityManagerService.java
frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
·??ContextImpl.java
frameworks/base/core/java/android/app/ContextImpl.java
·??ActivityThread.java
frameworks/base/core/java/android/app/ActivityThread.java
·??MediaStore.java
frameworks/base/core/java/android/provider/MediaStore.java
·??ContentResolver.java
frameworks/base/core/java/android/content/ContentResolver.java
·??ContentProvider.java
frameworks/base/core/java/android/content/ContentProvider.java
·??MediaProvider.java
package/providers/MediaProvider/src/java/com/android/MediaProvider/MediaProvider.java
·??SQLiteDatabase.java
frameworks/base/core/java/android/database/sqlite/SQLiteDatabase.java
·??SQLiteCompliteSql.java
frameworks/base/core/java/android/database/sqlite/SQLiteCompliteSql.java
·??android_database_SQLiteDatabase.cpp
frameworks/base/core/jni/android_database_SQLiteDatabase.cpp
·??android_database_SQLiteCompliteSql.cpp
frameworks/base/core/jni/android_database_SQLiteCompliteSql.cpp
·??sqlite3_android.cpp
external/sqlite3/android/sqlite3_android.cpp
·??SQLiteQueryBuilder.java
frameworks/base/core/java/android/database/sqlite/SQLiteQueryBuilder.java
·??SQLiteCursorDriver.java
frameworks/base/core/java/android/database/sqlite/SQLiteCursorDriver.java
·??SQLiteQuery.java
frameworks/base/core/java/android/database/sqlite/SQLiteQuery.java
·??SQLiteCursor.java
frameworks/base/core/java/android/database/sqlite/SQLiteCursor.java
·??SQLiteProgram.java
frameworks/base/core/java/android/database/sqlite/SQLiteProgram.java
·??CursorToBulkCursorAdaptor.java
frameworks/base/core/java/android/database/CursorToBulkCursorAdaptor.java
·??BulkCursorToCursorAdaptor.java
frameworks/base/core/java/android/database/BulkCursorToCursorAdaptor.java
·??CursorWindow.java
frameworks/base/core/java/android/database/CursorWindow.java
·??android_database_CursorWindow.cpp
frameworks/base/core/jni/android_database_CursorWindow.cpp
·??CursorWindow.cpp
frameworks/base/libs/binder/CursorWindow.cpp
·??android_database_SQLiteQuery.cpp
frameworks/base/core/jni/android_database_SQLiteQuery.cpp
·??CursorWrapper.java
frameworks/base/core/java/android/database/CursorWrapper.java
·??AbstractCursor.java
frameworks/base/core/java/android/database/AbstractCursor.java
·??BulkCursorNative.java
frameworks/base/core/java/android/database/BulkCursorNative.java
·??ParcelFileDescriptor.java
frameworks/base/core/java/android/os/ParcelFileDescriptor.java
·??MediaProvider.java
packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
·??android_util_Binder.cpp
frameworks/base/core/jni/android_util_Binder.cpp
·??Parcel.cpp
frameworks/base/libs/binder/Parcel.cpp
·??binder.c
kernel/drivers/staging/android/binder.c
本章重點(diǎn)分析ContentProvider、SQLite、Cursor query、close函數(shù)的實(shí)現(xiàn)及ContentResolver openAssetFileDescriptor函數(shù)。為了幫助讀者進(jìn)一步理解本章的知識(shí)點(diǎn),筆者特意挑選了四條分析路線(xiàn)。
·??第一條:以客戶(hù)端進(jìn)程通過(guò)MediaStore.Images.Media類(lèi)的靜態(tài)函數(shù)query來(lái)查詢(xún)MediaProvider中Image相關(guān)信息為入口點(diǎn),分析系統(tǒng)如何創(chuàng)建和啟動(dòng)MediaProvider。此分析路線(xiàn)著重關(guān)注客戶(hù)端進(jìn)程、ActivityManagerService及MediaProvider所在進(jìn)程間的交互。
·??第二條:沿襲第一條分析路徑,但是將關(guān)注焦點(diǎn)轉(zhuǎn)移到SQLiteDatabase如何創(chuàng)建數(shù)據(jù)庫(kù)的分析上。另外,本條路線(xiàn)還將對(duì)SQLite進(jìn)行相關(guān)介紹。
·??第三條:將重點(diǎn)研究Cursor query和close函數(shù)的實(shí)現(xiàn)細(xì)節(jié)。
·??第四條:將分析ContentResolver openAssetFileDescriptor函數(shù)的實(shí)現(xiàn)。
閑話(huà)少說(shuō),立即開(kāi)始本次分析之旅。
第一、二、三條分析路線(xiàn)都將以下面這段示例為參考。
[-->MediaProvider客戶(hù)端示例]
void QueryImage(Context context){
? //①得到ContentResolver對(duì)象
?ContentResolver cr = context.getContentResover();
? Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
? //②查詢(xún)數(shù)據(jù)庫(kù)
? Cursorcursor = MediaStore.Images.Media.query(cr,uri,null);
?cursor.moveToFirst();//③移動(dòng)游標(biāo)到頭部
? ......//從游標(biāo)中取出數(shù)據(jù)集
??cursor.close();//④關(guān)閉游標(biāo)
}
先介紹一下這段示例的情況:客戶(hù)端(即運(yùn)行本示例的進(jìn)程)查詢(xún)(query)的目標(biāo)ContentProvider是MediaProvider,它運(yùn)行于進(jìn)程android.process.media中。假設(shè)目標(biāo)進(jìn)程此時(shí)還未啟動(dòng)。
本節(jié)的關(guān)注點(diǎn)集中在:
·??MediaProvider所在進(jìn)程是如何創(chuàng)建的?MediaProvider又是如何創(chuàng)建的?
·??客戶(hù)端通過(guò)什么和位于目標(biāo)進(jìn)程中的MediaProvider交互的?
先來(lái)看第一個(gè)關(guān)鍵函數(shù)getContentResolver。
根據(jù)第6章對(duì)Context的介紹,Context的getContentResolver最終會(huì)調(diào)用它所代理的ContextImpl對(duì)象的getContentResolver函數(shù),此處直接看ContextImpl的代碼。
[-->ContextImpl.java::getContentResolver]
public ContentResolver getContentResolver() {
?? returnmContentResolver;
}
該函數(shù)直接返回mContentResolver,此變量在ContextImpl初始化時(shí)創(chuàng)建,相關(guān)代碼如下:
[-->ContextImpl.java::init]
final void init(LoadedApk packageInfo, IBinder activityToken,
?????? ActivityThreadmainThread, Resources container,String basePackageName) {
? ?......
??mMainThread = mainThread;//mainThread指向AcitivityThread對(duì)象
? //mContentResolver的真實(shí)類(lèi)型是ApplicationContentResolver
??mContentResolver = new ApplicationContentResolver(this, mainThread);
?? ......
?}
由以上代碼可知,mContentResolver的真實(shí)類(lèi)型是ApplicationContentResolver,它是ContextImpl定義的內(nèi)部類(lèi)并繼承了ContentResolver。
getContentResolver函數(shù)比較簡(jiǎn)單,就分析到此。下面來(lái)看第二個(gè)關(guān)鍵點(diǎn)。
提示為了書(shū)寫(xiě)方便,將ContentProvider簡(jiǎn)稱(chēng)為CP,將ContentResolver簡(jiǎn)稱(chēng)為CR。
?
第二個(gè)關(guān)鍵點(diǎn)是在MediaProvider客戶(hù)端示例中所調(diào)用的MediaStore.Image.Media 的query函數(shù)。MediaStore是多媒體開(kāi)發(fā)中常用的類(lèi),其內(nèi)部定義了專(zhuān)門(mén)針對(duì)Image、Audio、Video等不同多媒體信息的內(nèi)部類(lèi)來(lái)幫助客戶(hù)端開(kāi)發(fā)人員更好底和MediaProvider交互。這些類(lèi)及相互之間的關(guān)系如圖7-1所示。
http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter7/image001.png" alt="image" />
圖7-1? MediaStore類(lèi)圖
由圖7-1可知,MediaStore定義了較多的內(nèi)部類(lèi),我們重點(diǎn)展示作為內(nèi)部類(lèi)之一的Image的情況,其中:
·??MediaColumns定義了所有與媒體相關(guān)的數(shù)據(jù)庫(kù)表都會(huì)用到的數(shù)據(jù)庫(kù)字段,而ImageColumns定義了單獨(dú)針對(duì)Image的數(shù)據(jù)庫(kù)字段。
·??Image類(lèi)定義了一個(gè)名為Media的內(nèi)部類(lèi)用于查詢(xún)和Image相關(guān)的信息,同時(shí)Image類(lèi)還定義了一個(gè)名為T(mén)humbnails的內(nèi)部類(lèi)用于查詢(xún)和Image相關(guān)的縮略圖的信息(在Android平臺(tái)上,縮略圖的來(lái)源有兩種,一種是Image,另一種是Video,故Image定義了名為T(mén)humbnails的內(nèi)部類(lèi),而Video也定義了一個(gè)名為T(mén)humbnails的內(nèi)部類(lèi))。
提示MediaStore類(lèi)較為復(fù)雜,主要原因是它定義了一些同名類(lèi)。讀者閱讀代碼時(shí)務(wù)須仔細(xì)。
下面來(lái)看Image.Media的query函數(shù),其代碼非常簡(jiǎn)單,如下所示:
[-->MediaStore.java::Image.Media.query]
public static final class Media implementsImageColumns {
?? public static final Cursor query(ContentResolvercr,Uri uri,
?????????????????????????????????????????? String[]projection) {
? ?//直接調(diào)用ContentResolver的query函數(shù)
?? returncr.query(uri, projection, null,
???????????????????? ?null,DEFAULT_SORT_ORDER);
?}
Image.Media的query函數(shù)直接調(diào)用ContentResolver的query函數(shù),雖然cr的真實(shí)類(lèi)型是ApplicationContentResolver,但是此函數(shù)卻由其基類(lèi)ContentResolver實(shí)現(xiàn)。
提示追求運(yùn)行效率的程序員也許會(huì)對(duì)上邊這段代碼的實(shí)現(xiàn)略有微詞,因?yàn)镮mage.Media的query函數(shù)基本上沒(méi)做任何有意義的工作。假如客戶(hù)端直接調(diào)用cr.query函數(shù),則此處的query就增加了一次函數(shù)調(diào)用和返回的開(kāi)銷(xiāo)(即Image.Media query調(diào)用和返回時(shí)參數(shù)的入棧/出棧)。但是,通過(guò)Image.Media的封裝將使程序更清晰和易讀(與直接使用ContentResolver的query相比,代碼閱讀者一看Image.Media就知道其query函數(shù)應(yīng)該和Image有關(guān),否則需要通過(guò)解析uri參數(shù)才能確定查詢(xún)的信息是什么)。代碼清晰易讀和運(yùn)行效率高,往往是軟件開(kāi)發(fā)中的熊掌和魚(yú),它們之間的對(duì)立性,將在本章中體現(xiàn)得淋漓盡致。筆者建議讀者在實(shí)際開(kāi)發(fā)中結(jié)合具體情況決定取舍,萬(wàn)不可鉆牛角尖。
[-->ContentResolver.java::query]
public final Cursor query(Uri uri, String[]projection,
???????????String selection, String[] selectionArgs, String sortOrder) {
?? //調(diào)用acquireProvider函數(shù),參數(shù)為uri,函數(shù)也由ContentResolver實(shí)現(xiàn)
??IContentProvider provider = acquireProvider(uri);
? //注意:下面將和ContentProvider交互, 相關(guān)知識(shí)留待7.4節(jié)再分析
?? ......
?}
ContentResolver的query將調(diào)用acquireProvider,該函數(shù)定義在ContentResolver類(lèi)中,代碼如下:
[-->ContentResolver.java::query]
public final IContentProvider acquireProvider(Uriuri) {
??if(!SCHEME_CONTENT.equals(uri.getScheme()))?return null;
? Stringauth = uri.getAuthority();
? if (auth!= null) {
???? //acquireProvider是一個(gè)抽象函數(shù),由ContentResolver的子類(lèi)實(shí)現(xiàn)。在本例中,該函數(shù)
???? //將由ApplicationContentResolver實(shí)現(xiàn)。uri.getAuthority將返回代表目標(biāo)
??? ?//ContentProvider的名字
????? returnacquireProvider(mContext, uri.getAuthority());
? }
? returnnull;
?}
如上所述,acquireProvider由ContentResolver的子類(lèi)實(shí)現(xiàn),在本例中該函數(shù)由ApplicationContentResolver定義,代碼如下:
[-->ContextImpl.java::acquireProvider]
protected IContentProvider acquireProvider(Contextcontext, String name) {
???//mMainThread指向代表應(yīng)用進(jìn)程主線(xiàn)程的ActivityThread對(duì)象,每個(gè)應(yīng)用進(jìn)程只有一個(gè)
???//ActivityThread對(duì)象
?? ?return mMainThread.acquireProvider(context,name);
?}
如以上代碼所示,最終ActivityThread的acquireProvider函數(shù)將被調(diào)用,希望它不要再被層層轉(zhuǎn)包了。
ActivityThread的 acquireProvider函數(shù)的代碼如下:
[-->ActivityThread.java::acquireProvider]
public final IContentProvideracquireProvider(Context c, String name) {
? //①調(diào)用getProvider函數(shù),它很重要。見(jiàn)下文分析
??IContentProvider provider = getProvider(c,name);
? ......
? IBinderjBinder = provider.asBinder();
??synchronized(mProviderMap) {
???? //客戶(hù)端進(jìn)程將本進(jìn)程使用的ContentProvider信息保存到mProviderRefCountMap中,
???? //其主要功能與引用計(jì)數(shù)和資源釋放有關(guān),讀者暫可不理會(huì)它
?????ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
????? if(prc== null)
???????? ?mProviderRefCountMap.put(jBinder, newProviderRefCount(1));
?????else?? prc.count++;
?? }
?returnprovider;
}
在acquireProvider內(nèi)部調(diào)用getProvider得到一個(gè)IContentProvider類(lèi)型的對(duì)象,該函數(shù)非常重要,其代碼為:
[-->ActivityThread.java::getProvider]
private IContentProvider getProvider(Contextcontext, String name) {
? /*
?? 查詢(xún)?cè)搼?yīng)用進(jìn)程是否已經(jīng)保存了用于和遠(yuǎn)端ContentProvider通信的對(duì)象existing。
?? 此處,我們知道existing的類(lèi)型是IContentProvider,不過(guò)IContentProvider是一
?? 個(gè)interface,那么existing的真實(shí)類(lèi)型是什么呢?稍后再揭示
? */
?IContentProvider existing = getExistingProvider(context, name);
? if(existing != null) return existing;//如果existing存在,則直接返回
?
?IActivityManager.ContentProviderHolder holder = null;
? try {
????? //如果existing不存在,則需要向AMS查詢(xún),返回值的類(lèi)型為ContentProviderHolder
????? holder= ActivityManagerNative.getDefault().getContentProvider(
???????????????getApplicationThread(), name);
? }......
?? //注意:記住下面這個(gè)函數(shù)調(diào)用,此時(shí)是在客戶(hù)端進(jìn)程中
?IContentProvider prov = installProvider(context, holder.provider,
???????????????holder.info, true);
?? ......
? returnprov;
?}
以上代碼中讓人比較頭疼的是其中新出現(xiàn)的幾種數(shù)據(jù)類(lèi)型,如IContentProvider、ContentProviderHolder。先來(lái)分析AMS的getContentProvider。
getContentProvider的功能主要由getContentProviderImpl函數(shù)實(shí)現(xiàn),故此處可直接對(duì)它進(jìn)行分析。
getContentProviderImpl函數(shù)較長(zhǎng),可分段來(lái)看,先來(lái)分析下面一段代碼。
[-->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)......//如果查詢(xún)不到調(diào)用者信息,則拋出SecurityException
? ????}// if (caller != null)判斷結(jié)束
??? //name參數(shù)為調(diào)用進(jìn)程指定的代表目標(biāo)ContentProvider的authority
??? cpr =mProvidersByName.get(name);
??? //如果cpr不為空,表明該ContentProvider已經(jīng)在AMS中注冊(cè)
??? booleanproviderRunning = cpr != null;
??? if(providerRunning){
???????......//如果該ContentProvider已經(jīng)存在,則進(jìn)行對(duì)應(yīng)處理, 相關(guān)內(nèi)容可自行閱讀
??? }
?? //如果目標(biāo)ContentProvider對(duì)應(yīng)進(jìn)程還未啟動(dòng)
?? if(!providerRunning) {
????? ?try {
???????????//查詢(xún)PKMS,得到指定的ProviderInfo信息
???????????cpi = AppGlobals.getPackageManager().resolveContentProvider(
???????????????????name,STOCK_PM_FLAGS |
???????????????????PackageManager.GET_URI_PERMISSION_PATTERNS);
?????? ?}......
??????? String msg;
????? //權(quán)限檢查,此處不作討論
????? if((msg=checkContentProviderPermissionLocked(cpi, r)) != null)
?????????throw new SecurityException(msg);
???? /*
????? 如果system_server還沒(méi)啟動(dòng)完畢,并且該ContentProvider不運(yùn)行在system_server
????? 中,則此時(shí)不允許啟動(dòng)ContentProvider。讀者還記得哪個(gè)ContentProvider運(yùn)行在
?????system_server進(jìn)程中嗎?答案是SettingsProvider
? ??*/
???? .......
?
????ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
???? cpr =mProvidersByClass.get(comp);
???? finalboolean firstClass = cpr == null;
???? //初次啟動(dòng)MediaProvider對(duì)應(yīng)進(jìn)程時(shí),firstClass一定為true
??? ?if (firstClass) {
????? ???try {
?????????????//查詢(xún)PKMS,得到MediaProvider所在的Application信息
???????? ?????ApplicationInfoai =
??????????????????????AppGlobals.getPackageManager().getApplicationInfo(
??????????????????????????cpi.applicationInfo.packageName, STOCK_PM_FLAGS);
?????????? ??if (ai == null) return null;
????????????//在AMS內(nèi)部通過(guò)ContentProviderRecord來(lái)保存ContentProvider的信息,類(lèi)似
????????????//ActivityRecord,BroadcastRecord等
????????? ???cpr = new ContentProviderRecord(cpi, ai,comp);
?????? ??}......
??? }// if(firstClass)判斷結(jié)束
以上代碼的邏輯比較簡(jiǎn)單,主要是為目標(biāo)ContentProvider(即MediaProvider)創(chuàng)建一個(gè)ContentProviderRecord對(duì)象。結(jié)合第6章的知識(shí),AMS為四大組件都設(shè)計(jì)了對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),如ActivityRecord、BroadcastRecord等。
接著看getContentProviderImpl,其下一步的工作就是啟動(dòng)目標(biāo)進(jìn)程:
[-->ActivityManagerService.java::getContentProviderImpl]
?? /*
?? canRunHere函數(shù)用于判斷目標(biāo)CP能否運(yùn)行在r所對(duì)應(yīng)的進(jìn)程(即調(diào)用者所在進(jìn)程)
?? 該函數(shù)內(nèi)部做如下判斷:
?? (info.multiprocess|| info.processName.equals(app.processName))
????&& (uid == Process.SYSTEM_UID || uid == app.info.uid)
?? ?就本例而言,MediaProvider不能運(yùn)行在客戶(hù)端進(jìn)程中
?? */
??? if (r !=null && cpr.canRunHere(r)) ?returncpr;
?
??? finalint N = mLaunchingProviders.size();
??? ......//查找目標(biāo)ContentProvider對(duì)應(yīng)的進(jìn)程是否正處于啟動(dòng)狀態(tài)
??? //如果i大于等于N,表明目標(biāo)進(jìn)程的信息不在mLaunchingProviders中
??? if (i>= N) {
?????? finallong origId = Binder.clearCallingIdentity();
?????? ......
?????? //調(diào)用startProcessLocked函數(shù)創(chuàng)建目標(biāo)進(jìn)程
??????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;
?????? //將該進(jìn)程信息保存到mLaunchingProviders中
??????mLaunchingProviders.add(cpr);
??? }
?
???? if(firstClass) mProvidersByClass.put(comp, cpr);
????mProvidersByName.put(name, cpr);
???? /*
?????? 下面這個(gè)函數(shù)將為客戶(hù)端進(jìn)程和目標(biāo)CP進(jìn)程建立緊密的關(guān)系,即當(dāng)目標(biāo)CP進(jìn)程死亡后,
??????? AMS將根據(jù)該函數(shù)建立的關(guān)系找到客戶(hù)端進(jìn)程并殺死(kill)它們。在7.2.3節(jié)
?????? ?有對(duì)這個(gè)函數(shù)的相關(guān)解釋
???? */
???? incProviderCount(r, cpr);
???? if(cpr.launchingApp == null) return null;
???? try {
??????????cpr.wait();//等待目前進(jìn)程的啟動(dòng)
????? } ......
?? }// synchronized(this)結(jié)束
? returncpr;
}
通過(guò)對(duì)以上代碼的分析發(fā)現(xiàn),getContentProviderImpl將等待一個(gè)事件,想必讀者也能明白,此處一定是在等待目標(biāo)進(jìn)程啟動(dòng)并創(chuàng)建好MediaProvider。目標(biāo)進(jìn)程的這部分工作用專(zhuān)業(yè)詞語(yǔ)來(lái)表達(dá)就是發(fā)布目標(biāo)ContentProvider(即本例的MediaProvider)。
根據(jù)第6章的介紹,目標(biāo)進(jìn)程啟動(dòng)后要做的第一件大事就是調(diào)用AMS的attachApplication函數(shù),該函數(shù)的主要功能由attachApplicationLocked完成。我們回顧一下相關(guān)代碼。
[-->ActivityManagerService.java::attachApplicationLocked]
private final booleanattachApplicationLocked(IApplicationThread thread,
???????????int pid) {
?? ......
?? //通過(guò)PKMS查詢(xún)運(yùn)行在該進(jìn)程中的CP信息,并保存到mProvidersByClass中
?? Listproviders = normalMode ?
???????????????generateApplicationProvidersLocked(app) : null;
? //調(diào)用目標(biāo)應(yīng)用進(jìn)程的bindApplication函數(shù),此處將providers信息傳遞給目標(biāo)進(jìn)程
?? thread.bindApplication(processName,appInfo, providers,
?????????????????????????????app.instrumentationClass, profileFile,
???????????????????? ?????????......);
?? ......
}
再來(lái)看目標(biāo)進(jìn)程bindApplication的實(shí)現(xiàn),其內(nèi)部最終會(huì)通過(guò)handleBindApplication函數(shù)處理,我們回顧一下相關(guān)代碼。
[-->ActivtyThread.java::handleBindApplication]
private void handleBindApplication(AppBindDatadata) {
?? ......
?? if(!data.restrictedBackupMode){
???????List<ProviderInfo> providers = data.providers;
??????? if(providers != null) {
???????????//調(diào)用installContentProviders安裝
???????????installContentProviders(app, providers);
???????????......
?????? }
? ?}
?? ......
}
AMS傳遞過(guò)來(lái)的ProviderInfo列表將由目標(biāo)進(jìn)程的installContentProviders處理,其相關(guān)代碼如下:
[-->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()) {
???? //①也調(diào)用installProvider,注意該函數(shù)傳遞的第二個(gè)參數(shù)為null
????IContentProvider cp = installProvider(context, null, cpi, false);
???? if (cp!= null) {
?????????IActivityManager.ContentProviderHolder cph =
??????????? ??????????????newIActivityManager.ContentProviderHolder(cpi);
?????????cph.provider = cp;
??????????results.add(cph);//將信息添加到results數(shù)組中
?????????? ......//創(chuàng)建引用計(jì)數(shù)
???????? }
??? }//while循環(huán)結(jié)束
?
??? try {? //②調(diào)用AMS的publishContentProviders發(fā)布ContentProviders
???????????ActivityManagerNative.getDefault().publishContentProviders(
???????????????getApplicationThread(), results);
??????? } ......
?}
以上代碼列出了兩個(gè)關(guān)鍵點(diǎn),分別是:
·??調(diào)用installProvider得到一個(gè)IContentProvider類(lèi)型的對(duì)象。
·??調(diào)用AMS的publishContentProviders發(fā)布本進(jìn)程所運(yùn)行的ContentProvider。該函數(shù)留到后面再作分析
在繼續(xù)分析之前,筆者要特別強(qiáng)調(diào)installProvider,該函數(shù)既在客戶(hù)端進(jìn)程中被調(diào)用(還記得7.2.2節(jié)ActivityThread的acquireProvider函數(shù)中那句注釋嗎?),又在目標(biāo)進(jìn)程(即此處MediaProvider所在進(jìn)程)中被調(diào)用。與客戶(hù)端進(jìn)程的調(diào)用相比,只在一處有明顯的不同:
·??客戶(hù)端進(jìn)程調(diào)用installProvider函數(shù)時(shí),該函數(shù)的第二個(gè)參數(shù)不為null。
·??目標(biāo)進(jìn)程調(diào)用installProvider函數(shù)時(shí),該函數(shù)的第二個(gè)參數(shù)硬編碼為null。
我們?cè)?jīng)在6.2.3分析過(guò)installProvider函數(shù),結(jié)合那里的介紹可知:installProvider是一個(gè)通用函數(shù),不論客戶(hù)端使用遠(yuǎn)端的CP還是目標(biāo)進(jìn)程安裝運(yùn)行在其上的CP上,最終都會(huì)調(diào)用它,只不過(guò)參數(shù)不同罷了。
來(lái)看installProvider函數(shù),其代碼如下:
[-->ActivityThread.java::installProvider]
private IContentProvider installProvider(Contextcontext,
???????????IContentProvider provider, ProviderInfo info, boolean noisy) {
??ContentProvider localProvider = null;
?? if(provider == null) {//針對(duì)目標(biāo)進(jìn)程的情況
???????Context c = null;
????????ApplicationInfo ai = info.applicationInfo;
???????? if(context.getPackageName().equals(ai.packageName)) {
???????????????c = context;
???????????}......//這部分代碼已經(jīng)在6.2.3節(jié)分析過(guò)了,其目的就是為了得到正確的
??????????//Context用于加載Java字節(jié)碼
???????? try{
????????????final java.lang.ClassLoader cl = c.getClassLoader();
????????????//通過(guò)Java反射機(jī)制創(chuàng)建MediaProvider實(shí)例
????????????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)判斷結(jié)束
??? /*
???? 由以上代碼可知,對(duì)于provider不為null 的情況(即客戶(hù)端調(diào)用的情況),該函數(shù)沒(méi)有
???? 什么特殊的處理
??? */
??? ......
???? /*
????? 引用計(jì)數(shù)及設(shè)置DeathReceipient等相關(guān)操作。在6.2.3節(jié)的第2個(gè)小標(biāo)題中
????? 曾說(shuō)過(guò),目標(biāo)進(jìn)程為自己進(jìn)程中的CP實(shí)例設(shè)置DeathReceipient沒(méi)有作用,因?yàn)槎咴谕粋€(gè)
????? 進(jìn)程中,自己怎么能接收自己的訃告消息呢?不過(guò),如果客戶(hù)端進(jìn)程為目標(biāo)進(jìn)程的CP設(shè)置
???? DeathReceipient又有作用嗎?仔細(xì)思考這個(gè)問(wèn)題
??? */
??? returnprovider;//最終返回的對(duì)象是IContentProvider類(lèi)型,它到底是什么呢?
?}
由以代碼可知,installProvider最終返回的是一個(gè)IContentProvider類(lèi)型的對(duì)象。對(duì)于目標(biāo)進(jìn)程而言,該對(duì)象是通過(guò)調(diào)用CP的實(shí)例對(duì)象的(本例就是MediaProvider) getIContentProvider函數(shù)得來(lái)的。而對(duì)于客戶(hù)端進(jìn)程而言,該對(duì)象是由installProvider第二個(gè)參數(shù)傳遞進(jìn)來(lái)的,那么,這個(gè)IContentProvider到底是什么?
要說(shuō)清楚IContentProvider,就先來(lái)看ContentProvider家族的類(lèi)圖,如圖7-2所示。
http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter7/image002.png" alt="image" />
圖7-2? ContentProvider類(lèi)圖
圖7-2揭示了IContentProvider的真面目,具體介紹如下:
·??每個(gè)ContentProvider實(shí)例中都有一個(gè)mTransport成員,其類(lèi)型為T(mén)ransport。
·??Transport類(lèi)從ContentProviderNative派生。由圖7-2可知,ContentProviderNative從Binder類(lèi)派生,并實(shí)現(xiàn)了IContentProvider接口。結(jié)合前面的代碼,IContentProvider將是客戶(hù)端進(jìn)程和目標(biāo)進(jìn)程交互的接口,即目標(biāo)進(jìn)程使用IContentProvider的Bn端Transport,而客戶(hù)端使用IContentProvider的Bp端,其類(lèi)型是ContentProviderProxy(定義在ContentProviderNative.java中)。
客戶(hù)端如何通過(guò)IContentProvider query函數(shù)和目標(biāo)CP進(jìn)程交互的呢?其流程如下:
·??CP客戶(hù)端得到IContentProvider的Bp端(實(shí)際類(lèi)型是ContentProviderProxy),并調(diào)用其query函數(shù),在該函數(shù)內(nèi)部將參數(shù)信息打包,傳遞給Transport(它是IContentProvider的Bn端)。
·??Transport的onTransact函數(shù)將調(diào)用Transport的query函數(shù),而Transport的query函數(shù)又將調(diào)用ContentProvider子類(lèi)定義的query函數(shù)(即MediaProvider的query函數(shù))。
關(guān)于目標(biāo)進(jìn)程這一系列的調(diào)用函數(shù),不妨先看看Transport的query函數(shù),其代碼為:
[-->ContentProvider.java::Transport.query]
public Cursor query(Uri uri, String[] projection,
???????????????String selection, String[] selectionArgs, String sortOrder) {
??enforceReadPermission(uri);
??//Transport為ContentProvider的內(nèi)部類(lèi),此處將調(diào)用ContentProvider的query函數(shù)
?? //本例中,該query函數(shù)由MediaProvider實(shí)現(xiàn),故最終會(huì)調(diào)用MediaProvider的query
?? returnContentProvider.this.query(uri, projection, selection,
???????????????????selectionArgs, sortOrder);
?}
務(wù)必弄清楚,此處只有一個(gè)目標(biāo)ContentProvider的實(shí)例,即只有一個(gè)MediaProvider對(duì)象。Transport的 query函數(shù)內(nèi)部雖然調(diào)用的是基類(lèi)ContentProvider的query函數(shù),但是根據(jù)面向?qū)ο蟮亩鄳B(tài)原理,該函數(shù)最終由其子類(lèi)(本例中是MediaProvider)來(lái)實(shí)現(xiàn)。
認(rèn)識(shí)了IContentProvider,即知道了客戶(hù)端進(jìn)程和目標(biāo)進(jìn)程的交互接口。
繼續(xù)我們的分析。此時(shí)目標(biāo)進(jìn)程需要將MediaProvider的信息通過(guò)AMS發(fā)布出去。
要把目標(biāo)進(jìn)程的CP信息發(fā)布出去,需借助AMS 的pulishContentProviders函數(shù),其代碼如下:
[-->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) {
????????? ?......//將相關(guān)信息分別保存到mProviderByClass和mProvidersByName中
???????? ??int NL = mLaunchingProviders.size();
?????????...... //目標(biāo)進(jìn)程已經(jīng)啟動(dòng),將其從mLaunchingProviders移除
?????????synchronized (dst) {
????????????dst.provider = src.provider;//將信息保存在dst中
????????????dst.proc = r;
???????????//觸發(fā)還等在(wait)getContentProvider中的那個(gè)客戶(hù)端進(jìn)程
????????????dst.notifyAll();
? ????????}
????? ?updateOomAdjLocked(r);//調(diào)節(jié)目標(biāo)進(jìn)程的oom_adj等相關(guān)參數(shù)
???? }// if(dst != null)判斷結(jié)束
?? ......
?? }
}
至此,客戶(hù)端進(jìn)程將從getContentProvider中返回,并調(diào)用installProvider函數(shù)。根據(jù)前面的分析,客戶(hù)端進(jìn)程調(diào)用installProvider時(shí),其第二個(gè)參數(shù)不為null,即客戶(hù)端進(jìn)程已經(jīng)從AMS中得到了能直接和目標(biāo)進(jìn)程交互的IContentProvider Bp端對(duì)象。此后,客戶(hù)端就可直接使用該對(duì)象向目標(biāo)進(jìn)程發(fā)起請(qǐng)求。
回顧一下整個(gè)MediaProvider的啟動(dòng)和創(chuàng)建過(guò)程,如圖7-3所示。
http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter7/image003.png" alt="image" />
圖7-3? MediaProvider的啟動(dòng)和創(chuàng)建流程
整個(gè)流程相對(duì)比較簡(jiǎn)單。讀者在分析時(shí)只要注意installProvider這個(gè)函數(shù)在目標(biāo)進(jìn)程和客戶(hù)端進(jìn)程中被調(diào)用時(shí)的區(qū)別即可。這里再次強(qiáng)調(diào):
·??目標(biāo)進(jìn)程調(diào)用installProvider時(shí),傳遞的第二個(gè)參數(shù)為null,使內(nèi)部通過(guò)Java反射機(jī)制真正創(chuàng)建目標(biāo)CP實(shí)例。
·??客戶(hù)端調(diào)用installProvider時(shí),其第二個(gè)參數(shù)已經(jīng)通過(guò)查詢(xún)AMS得到。該函數(shù)真正的工作只不過(guò)是引用計(jì)數(shù)控制和設(shè)置訃告接收對(duì)象罷了。
至此,客戶(hù)端進(jìn)程和目標(biāo)進(jìn)程通信的通道IContentProvider已經(jīng)登場(chǎng)。除此之外,客戶(hù)端進(jìn)程和目標(biāo)CP還建立了非常緊密的關(guān)系,這種關(guān)系造成的后果就是一旦目標(biāo)CP進(jìn)程死亡,AMS會(huì)殺死與之有關(guān)的客戶(hù)端進(jìn)程。不妨回顧一下與之相關(guān)的知識(shí)點(diǎn):
·??該關(guān)系的建立是在AMS getContentProviderImpl函數(shù)中調(diào)用incProviderCount完成的,關(guān)系的確立以ContentProviderRecorder保存客戶(hù)端進(jìn)程的ProcessRecord信息為標(biāo)識(shí)。
·??一旦CP進(jìn)程死亡,AMS能根據(jù)該ContentProviderRecorder中保存的客戶(hù)端信息找到使用該CP的所有客戶(hù)端進(jìn)程,然后再殺死它們。
客戶(hù)端能否撤銷(xiāo)這種緊密關(guān)系呢?答案是肯定的,但這和Cursor是否關(guān)閉有關(guān)。這里先簡(jiǎn)單描述一下流程:
·??當(dāng)Cursor關(guān)閉時(shí),ContextImpl的releaseProvider會(huì)被調(diào)用。根據(jù)前面的介紹,它最終會(huì)調(diào)用ActivityThread的releaseProvider函數(shù)。
·??ActivityThread的releaseProvider函數(shù)會(huì)導(dǎo)致completeRemoveProvider被調(diào)用,在其內(nèi)部根據(jù)該CP的引用計(jì)數(shù)判斷是否需要調(diào)用AMS的removeContentProvider。
·??通過(guò)AMS的removeContentProvider將刪除對(duì)應(yīng)ContentProviderRecord中此客戶(hù)端進(jìn)程的信息,這樣一來(lái),客戶(hù)端進(jìn)程和目標(biāo)CP進(jìn)程的緊密關(guān)系就蕩然無(wú)存了。
至此,本章第一條分析路線(xiàn)就介紹完畢。
提示讀者可能覺(jué)得,這條路線(xiàn)是對(duì)第6章的補(bǔ)充和延續(xù)。不過(guò),雖然目標(biāo)進(jìn)程由AMS創(chuàng)建和啟動(dòng),而且ContentProvider的發(fā)布也需和AMS交互,但是對(duì)于ContentProvider來(lái)說(shuō),我們更關(guān)注客戶(hù)端和目標(biāo)進(jìn)程中ContentProvider實(shí)例間的交互。事實(shí)上,客戶(hù)端得到IContentProvider Bp端對(duì)象后,即可直接與目標(biāo)進(jìn)程的CP實(shí)例交互,也就無(wú)需借助AMS了,所以筆者將這條路線(xiàn)放到了本章進(jìn)行分析。
作為Android多媒體系統(tǒng)中媒體信息的倉(cāng)庫(kù),MediaProvider使用了SQLite數(shù)據(jù)庫(kù)來(lái)管理系統(tǒng)中多媒體相關(guān)的數(shù)據(jù)信息。作為第二條分析路線(xiàn),本節(jié)的目標(biāo)是分析MediaProvider如何利用SQLite創(chuàng)建數(shù)據(jù)庫(kù),同時(shí)還將介紹和SQLite相關(guān)的一些知識(shí)點(diǎn)。
先來(lái)看大名鼎鼎的SQLite及Java層的SQLiteDatabase家族。
SQLite是一個(gè)輕量級(jí)的數(shù)據(jù)庫(kù),它和筆者之前接觸的SQLServer或Oracle DB比起來(lái),猶如螞蟻和大象的區(qū)別。它“輕”到什么程度呢?筆者總結(jié)了SQLite具有的兩個(gè)特點(diǎn):
·??從代碼上看,SQLite所有的功能都實(shí)現(xiàn)在Sqlite3.c中,而頭文件Sqlite3.h定義了它所支持的API。其中,Sqlite3.c文件包含12萬(wàn)行左右的代碼,相當(dāng)于一個(gè)中等偏小規(guī)模的程序。
·??從使用者角度的來(lái)說(shuō),SQLite編譯完成后將生成一個(gè)libsqlite.so,大小僅為300多KB。
SQLite確實(shí)夠輕,但這個(gè)“輕”還不是本節(jié)標(biāo)題“輕裝上陣”一詞中的“輕”。為什么?此處先向讀者們介紹一個(gè)直接使用SQLite API開(kāi)發(fā)的Android native程序示例,該示例最終編譯成的二進(jìn)制可執(zhí)行程序名為sqlitetest。
注意本書(shū)后文所說(shuō)的SQLite API特指libsqlite.so提供的Native層的API。
使用SQLite API開(kāi)發(fā)的Android native程序示例的代碼如下:
[-->SqliteTest.cpp::libsqlite示例]
#include <unistd.h>
#include <sqlite3.h> //包含sqlite API頭文件,這里的3是SQLite的版本號(hào)
#include <stdlib.h>
?
#define LOG_TAG "SQLITE_TEST"? //定義該進(jìn)程logcat輸出的標(biāo)簽
#include <utils/Log.h>
#ifndef NULL
?? #defineNULL (0)
#endif
//聲明數(shù)據(jù)庫(kù)文件的路徑
#define DB_PATH"/mnt/sdcard/sqlite3test.db"
/*
?? 聲明一個(gè)全局的SQLite句柄,開(kāi)發(fā)者無(wú)需了解該數(shù)據(jù)結(jié)構(gòu)的具體內(nèi)容,只要知道它代表了使用者
?? 和數(shù)據(jù)庫(kù)的一種連接關(guān)系即可。以后凡是針對(duì)特定數(shù)據(jù)庫(kù)的操作,都需要傳入對(duì)應(yīng)的SQLite句柄
*/
static sqlite3* g_pDBHandle = NULL;
?
/*
?? 定義一個(gè)宏,用于檢測(cè)SQLite API調(diào)用的返回值,如果value不等于expectValue,則打印警告,
?? 并退出程序。注意,進(jìn)程退出后,系統(tǒng)會(huì)自動(dòng)回收分配的內(nèi)存資源。對(duì)如此簡(jiǎn)單的例子來(lái)說(shuō),讀者大可
??? 不必苛責(zé)。其中,sqlite3_errmsg函數(shù)用于打印錯(cuò)誤信息
*/
#define CHECK_DB_ERR(value,expectValue) \
do \
{ \
?? if(value!= expectValue)\
?? {\
????LOGE("Sqlite db fail:%s",g_pDBHandle==NULL?"db not \
??? ???????connected":sqlite3_errmsg(g_pDBHandle));\
????exit(0);\
?? }\
}while(0)
?
?
int main(int argc, char* argv[])
{
??LOGD("Delete old DB file");
??unlink(DB_PATH);//先刪除舊的數(shù)據(jù)庫(kù)文件
??LOGD("Create new DB file");
?? /*
??? 調(diào)用sqlite3_open創(chuàng)建一個(gè)數(shù)據(jù)庫(kù),并將和該數(shù)據(jù)庫(kù)的連接環(huán)境保存在全局的SQLite句柄
???? g_pDBHandle中,以后操作g_pDBHandle就是操作DB_PATH對(duì)應(yīng)的數(shù)據(jù)庫(kù)
??? */
?? int ret =sqlite3_open(DB_PATH,&g_pDBHandle);
??CHECK_DB_ERR(ret,SQLITE_OK);
??
??LOGD("Create Table personal_info:");
?? /*
??? 定義宏TABLE_PERSONAL_INFO,用于描述為本例數(shù)據(jù)庫(kù)建立一個(gè)表所用的SQL語(yǔ)句,
??? 不熟悉SQL語(yǔ)句的讀者可先學(xué)習(xí)相關(guān)知識(shí)。從該宏的定義可知,將建立一個(gè)名為
???personal_info的表,該表有4列,第一列是主鍵,類(lèi)型是整型(SQLite中為INTEGER),
??? 每加入一行數(shù)據(jù)該值會(huì)自動(dòng)遞增;第二列名為"name",數(shù)據(jù)類(lèi)型是字符串(SQLite中為T(mén)EXT);
???? 第三列名為“age”,數(shù)據(jù)類(lèi)型是整型;第四列名為“sex”,數(shù)據(jù)類(lèi)型是字符串
?? */
?? #defineTABLE_PERSONAL_INFO? \
?? ???????"CREATETABLE personal_info" \
?? ????????"(ID INTEGER primary keyautoincrement," \
?????????? "nameTEXT," \
?? ????????"age INTEGER,"\
?? ????????"sex TEXT"\
?? ????????")"
?? //打印TABLE_PERSONAL_INFO所對(duì)應(yīng)的SQL語(yǔ)句
??LOGD("\t%s\n",TABLE_PERSONAL_INFO);
? //調(diào)用sqlite3_exec執(zhí)行前面那條SQL語(yǔ)句
?? ret =sqlite3_exec(g_pDBHandle,TABLE_PERSONAL_INFO,NULL,NULL,NULL);
??CHECK_DB_ERR(ret,SQLITE_OK);
??
?? /*
??? 定義插入一行數(shù)據(jù)所使用的SQL語(yǔ)句,注意最后一行中的問(wèn)號(hào),它表示需要在插入數(shù)據(jù)
?? 前和具體的值綁定。插入數(shù)據(jù)庫(kù)對(duì)應(yīng)的SQL語(yǔ)句是標(biāo)準(zhǔn)的INSERT命令
?? */
??LOGD("Insert one personal info into personal_info table");
?? #defineINSERT_PERSONAL_INFO? \
??"INSERT INTO personal_info"\
??"(name,age,sex)"\
??"VALUES"\
??"(?,?,?)"?? //注意這一行語(yǔ)句中的問(wèn)號(hào)
??LOGD("\t%s\n",INSERT_PERSONAL_INFO);
?
?? //sqlite3_stmt是SQLite中很重要的一個(gè)結(jié)構(gòu)體,它代表了一條SQL語(yǔ)句
?? sqlite3_stmt* pstmt = NULL;
?? /*
??? 調(diào)用sqlite3_prepare初始化pstmt,并將其和INSERT_PERSONAL_INFO綁定。
??? 也就是說(shuō),如果執(zhí)行pstmt,就會(huì)執(zhí)行INSERT_PERSONAL_INFO語(yǔ)句
?? */
?? ret =sqlite3_prepare(g_pDBHandle,INSERT_PERSONAL_INFO,-1,&pstmt,NULL);
??CHECK_DB_ERR(ret,SQLITE_OK);
? /*
??? 調(diào)用sqlite3_bind_xxx為該pstmt中對(duì)應(yīng)的問(wèn)號(hào)綁定具體的值,該函數(shù)的第二個(gè)參數(shù)用于
??? 指定第幾個(gè)問(wèn)號(hào)
? */
?? ret =sqlite3_bind_text(pstmt,1,"dengfanping",-1,SQLITE_STATIC);
?? ret =sqlite3_bind_int(pstmt,2,30);
?? ret =sqlite3_bind_text(pstmt,3,"male",-1,SQLITE_STATIC);
?? //調(diào)用sqlite3_step執(zhí)行對(duì)應(yīng)的SQL語(yǔ)句,該函數(shù)如果執(zhí)行成功,我們的personal_info
?? //表中將添加一條新記錄,對(duì)應(yīng)值為(1,dengfanping,30,male)
?? ret =sqlite3_step(pstmt);
??CHECK_DB_ERR(ret,SQLITE_DONE);
?? //調(diào)用sqlite3_finalize銷(xiāo)毀該SQL語(yǔ)句
?? ret =sqlite3_finalize(pstmt);
??
?? //下面將從表中查詢(xún)name為"dengfanping"的person的age值
??LOGD("select dengfanping's age from personal_info table");
?? pstmt =NULL;
?? /*
??? 重新初始化該pstmt,并將其和SQL語(yǔ)句“SELECT age FROM personal_info WHERE name
???? = ?”綁定
?? */
?? ret =sqlite3_prepare(g_pDBHandle,"SELECT age FROM personal_info WHERE name
???????????????????????????????????????????? ?=? ",-1,&pstmt,NULL);
??CHECK_DB_ERR(ret,SQLITE_OK);??
?? /*
??? 綁定pstmt中第一個(gè)問(wèn)號(hào)為字符串“dengfanping”,最終該SQL語(yǔ)句為
??? SELECTage FROM personal_info WHERE name = 'dengfanping'
?? */
?? ret =sqlite3_bind_text(pstmt,1,"dengfanp