鍍金池/ 教程/ Android/ 第8章? 深入理解ContentService 和 AccountManagerService
第2章? 深入理解 Java Binder 和 MessageQueue
第7章? 深入理解 ContentProvider
第5章? 深入理解 PowerManagerService
第3章? 深入理解 SystemServer
第8章? 深入理解ContentService 和 AccountManagerService
第1章?開發(fā)環(huán)境部署
第4章? 深入理解 PackageManagerService
第6章?深入理解ActivityManagerService

第8章? 深入理解ContentService 和 AccountManagerService

本章主要內(nèi)容:

·??介紹ContentService

·??介紹AccountManagerService

本章所涉及的源代碼文件名及位置:

·??SystemServer.java

frameworks/base/services/java/com/android/server/SystemServer.java

·??ContentService.java

frameworks/base/core/java/android/content/ContentService.java

·??ContentResolver.java

frameworks/base/core/java/android/content/ContentResolver.java

·??UsbSettings.java

packages/apps/Settings/src/com/android/settings/deviceinfo/UsbSettings.java

·??DevelopmentSettings.java

packages/apps/Settings/src/com/android/settings/DevelopmentSettings.java

·??UsbDeviceManager.java

frameworks/base/services/java/com/android/server/usb/UsbDeviceManager.java

·??AccountManagerService.java

frameworks/base/core/java/android/accounts/AccountManagerService.java

·??AccountAuthenticatorCache.java

frameworks/base/core/java/android/accounts/AccountAuthenticatorCache.java

·??RegisteredServicesCache.java

frameworks/base/core/java/android/content/pm/RegisteredServicesCache.java

·??AccountManager.java

frameworks/base/core/java/android/accounts/AccountManager.java

·??EasAuthenticatorService

packages/apps/Email/src/com/android/email/service/EasAuthenticatorService.java

·??SyncManager.java

frameworks/base/core/java/android/content/SyncManager.java

·??SyncStorageEngine.java

frameworks/base/core/java/android/content/SyncStorageEngine.java

·??SyncAdapterCache.java

frameworks/base/core/java/android/content/SyncAdapterCache.java

·??SyncQueue.java

frameworks/base/core/java/android/content/SyncQueue.java

·??EmailSyncAdapterService.java

packages/apps/Exchange/src/com/android/exchange/EmailSyncAdapterService.java

·??AbstractThreadedSyncAdapter.java

frameworks/base/core/java/android/content/AbstractThreadedSyncAdapter.java

8.1 ?概述

本章將分析ContentService和AccountManagerService,其中,ContentService包含以下兩個(gè)主要功能:

·??它是Android平臺(tái)中數(shù)據(jù)更新通知的執(zhí)行者。數(shù)據(jù)更新通知與第7章分析Cursorquery函數(shù)實(shí)現(xiàn)時(shí)提到的ContentObserver有關(guān)。這部分內(nèi)容將在8.2節(jié)中分析。

·??它是Android平臺(tái)中數(shù)據(jù)同步服務(wù)的管理中樞。當(dāng)用戶通過Android手機(jī)中的Contacts應(yīng)用將聯(lián)系人信息同步到遠(yuǎn)端服務(wù)器時(shí),就需要和ContentService交互。這部分內(nèi)容是本章的難點(diǎn),將在8.4節(jié)中進(jìn)行分析

本章要分析的第二個(gè)Service是AccountManagerService,它負(fù)責(zé)管理Android手機(jī)中用戶的賬戶,這些賬戶是用戶的online賬戶,例如用戶在Google、Facebook上注冊(cè)的賬戶。

本章將先分析ContentService中數(shù)據(jù)通知機(jī)制的實(shí)現(xiàn),然后分析AccountManagerService,最后再介紹ContentService中的數(shù)據(jù)同步服務(wù)。

提示這兩個(gè)Service的難度都不大,它們?cè)谠O(shè)計(jì)結(jié)構(gòu)上有較大的相似性,在內(nèi)容上也有一定的關(guān)聯(lián)。另外,作為本書最后一章,筆者照例會(huì)留一些難度適中的問題或知識(shí)點(diǎn)供讀者自行分析研究。

8.2? 數(shù)據(jù)更新通知機(jī)制分析

何為數(shù)據(jù)更新通知?先來看日常生活中的一個(gè)例子。

筆者所在公司采用BugZilla來管理Bug。在日常工作中,筆者和同事們的一部分工作就是登錄BugZilla查詢各自名下的Bug并修改它們。如何跟蹤自己的Bug呢?其實(shí),以上描述中已經(jīng)提到了一種方法,即登錄BugZilla并查詢。除此之外,BugZilla還支持另一種方法,即為每個(gè)Bug設(shè)置一個(gè)關(guān)系人列表,一旦該Bug的狀態(tài)發(fā)生變化,BugZilla就會(huì)給該Bug關(guān)系人列表中的人發(fā)送郵件。

上例中提到的第二種方法就是本節(jié)要分析的數(shù)據(jù)更新通知機(jī)制。一般說來,領(lǐng)導(dǎo)和項(xiàng)目經(jīng)理(PM)使用第一種方法居多,因?yàn)樗麄冃枰欢〞r(shí)地查詢和統(tǒng)計(jì)全局Bug的情況。而程序員使用第二種方法較多(也許是沒辦法的事情吧,誰會(huì)情愿主動(dòng)查詢自己的Bug呢?)。

類似的通知機(jī)制在日常生活中的其他地方還有使用。在操作系統(tǒng)中,這種通知機(jī)制同樣也廣泛存在。例如,在OS中,設(shè)計(jì)人員一般會(huì)安排外部設(shè)備以中斷的方式通知CPU并讓其開展后續(xù)處理,而不會(huì)讓CPU去輪詢外設(shè)的狀態(tài)。

現(xiàn)在回到Android平臺(tái),如果程序需要監(jiān)控某數(shù)據(jù)項(xiàng)的變化,可以采用一個(gè)類似while循環(huán)的語句不斷查詢它以判斷其值是否發(fā)生了變化。顯而易見,這種方式的效率很低。有了通知機(jī)制以后,程序只需注冊(cè)一個(gè)ContentObserver實(shí)例即可。一旦該項(xiàng)數(shù)據(jù)發(fā)生變化,系統(tǒng)就會(huì)通過ContentObserver的onChange函數(shù)來通知我們。與前面所述的輪詢相比,此處的通知方式明顯更高效。

通過上面的描述可以知道,通知機(jī)制的實(shí)施包括兩個(gè)步驟:第一步,注冊(cè)觀察者;第二步,通知觀察者。在Android平臺(tái)中,這兩步都離不開ContentService,下面來認(rèn)識(shí)一下它。

提示在設(shè)計(jì)模式中,通知機(jī)制對(duì)應(yīng)的模式是Observer模式,即觀察者模式。

8.2.1? 初識(shí)ContentService

SystemServer創(chuàng)建ContentService的代碼非常簡(jiǎn)單,如下所示:

[-->SystemServer::ServerThread.run]

public void run() {

?? ......

??ContentService.main(context,

???????????????????factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL);

?? ......

}

以上代碼中直接調(diào)用了ContentService的main函數(shù)。在一般情況下,該函數(shù)第二個(gè)參數(shù)為false。此main函數(shù)的代碼如下:

·??[-->ContentService.java::main]

public static IContentService main(Contextcontext, boolean factoryTest) {

? ?//構(gòu)造ContentService實(shí)例

??ContentService service = new ContentService(context, factoryTest);

?? //將ContentService注冊(cè)到ServiceManager中,其注冊(cè)名叫“content”

??ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME,

??????????????????????????????? ?service);

?? returnservice;

}

ContentService的構(gòu)造函數(shù)的代碼如下:

[-->ContentService.java::ContentService]

ContentService(Context context, booleanfactoryTest) {

?? ?mContext = context;

???mFactoryTest = factoryTest;

?? ?getSyncManager(); //獲取同步服務(wù)管理對(duì)象,接下來看它的代碼

}

[-->ContentService.java::getSyncManager]

private SyncManager getSyncManager() {

???synchronized(mSyncManagerLock) {

???? try {

??????????//創(chuàng)建一個(gè)SyncManager實(shí)例,它是ContentService中負(fù)責(zé)數(shù)據(jù)同步服務(wù)的

??????????//主力成員。留待8.4節(jié)再詳細(xì)分析它

???????????if (mSyncManager == null) mSyncManager = new

??????????????????????????????????????? SyncManager(mContext,mFactoryTest);

?????? }......

???????????return mSyncManager;

???? }

}

看完以上代碼讀者可能會(huì)覺得ContentService比較簡(jiǎn)單。其實(shí),ContentService中最難的功能是數(shù)據(jù)同步服務(wù),不過該功能的實(shí)現(xiàn)都封裝在SyncManager及相關(guān)類中了,所以在分析通知機(jī)制時(shí)不會(huì)和數(shù)據(jù)同步服務(wù)有太多瓜葛。

下面來分析通知機(jī)制實(shí)施的第一步,注冊(cè)ContentObserver。該步驟由ContentResovler提供的registerContentObserver函數(shù)來實(shí)現(xiàn)。

8.2.2? ContentResovler的registerContentObserver分析

[-->ContentResolver.java::registerContentObserver]

public final void registerContentObserver(Uri uri,

??????????? booleannotifyForDescendents,ContentObserver observer){

? /*

? ?注意registerContentObserver傳遞的參數(shù),其中:

?? uri是客戶端設(shè)置的它所需要監(jiān)聽的數(shù)據(jù)項(xiàng)的地址,用Uri來表示

?? notifyForDescendents:如果該值為true,則所有地址包含此uri的數(shù)據(jù)項(xiàng)發(fā)生變化時(shí)

?? 都會(huì)觸發(fā)通知。否則只有完全符合該uri地址的數(shù)據(jù)項(xiàng)發(fā)生變化時(shí)才會(huì)觸發(fā)通知。以文件夾和

?? 其中的文件為例,若uri指向某文件夾,則需設(shè)置notifyForDescendents為true。即該文件

?? 夾下的任何文件發(fā)生變化,都需要通知監(jiān)聽者。

?? observer是客戶端設(shè)置的監(jiān)聽對(duì)象。當(dāng)數(shù)據(jù)項(xiàng)發(fā)生變化時(shí),該對(duì)象的onChange函數(shù)將被調(diào)用

? */

? try {???

???????????/*

????????????調(diào)用ContentService的registerContentObserver函數(shù),其第三個(gè)參數(shù)是

??????????? ?observer.getContentObserver的返回值,它是什么呢?

???????????*/

???????????getContentService().registerContentObserver(uri,

?????????????????????????????????? notifyForDescendents,

????? ?????????????????????????????observer.getContentObserver());

??????? } ......

?}

registerContentObserver最終將調(diào)用ContentService的registerContentObserver函數(shù),其中第三個(gè)參數(shù)是ContentObservergetContentObserver的返回值。這個(gè)返回值是什么呢?需請(qǐng)出ContentObserver家族成員。

1.? ContentObserver介紹

ContentObserver家族成員如圖8-1所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter8/image001.png" alt="image" />

圖8-1? ContentObserver家族類圖

圖8-1中的ContentObserver類和第7章中介紹的ContentProvider類非常類似,內(nèi)部都定義了一個(gè)Transport類參與Binder通信。由圖8-1可知,Transport類從IContentObserver.stub派生。從Binder通信角度來看,客戶端進(jìn)程中的Transport將是Bn端。如此,通過registerContentObserver傳遞到ContentService所在進(jìn)程的就是Bp端。IContentObserverBp端對(duì)象的真實(shí)類型是IContentObserver.Stub.Proxy。

注意IContentObserver.java由aidl處理IContentObserver.aidl生成,其位置在out/targer/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/database/IContentObserver.java中。

2.? registerContentObserver函數(shù)分析

下面來看ContentService的registerContentObserver函數(shù)的代碼。

[-->ContentService.java::registerContentObserver]

public void registerContentObserver(Uri uri,boolean notifyForDescendents,

????? ?????????IContentObserver observer) {

?? ??......

???synchronized (mRootNode) {

????//ContentService要做的事情其實(shí)很簡(jiǎn)單,就是保存uri和observer的對(duì)應(yīng)關(guān)系到

???? //其內(nèi)部變量mRootNode中

????mRootNode.addObserverLocked(uri, observer, notifyForDescendents,

????????????????????mRootNode, Binder.getCallingUid(),

???????????????????Binder.getCallingPid());

?}

mRootNode是ContentService的成員變量,其類型為ObserverNode。ObserverNode的組織形式是數(shù)據(jù)結(jié)構(gòu)中的樹,其葉子節(jié)點(diǎn)的類型為ObserverEntry,它保存了uri和對(duì)應(yīng)的IContentObserver對(duì)象。本節(jié)不關(guān)注它們的內(nèi)部實(shí)現(xiàn),讀者若有興趣,不妨自行研究。

至此,客戶端已經(jīng)為某數(shù)據(jù)項(xiàng)設(shè)置了ContentObserver。再來看通知機(jī)制實(shí)施的第二步,即通知觀察者。

8.2.3? ContentResolver的 notifyChange分析

數(shù)據(jù)更新的通知由ContentResolver的notifyChange函數(shù)觸發(fā)??碝ediaProvider的update函數(shù)的代碼如下:

[-->MediaProvider.java::update]

public int update(Uri uri, ContentValuesinitialValues, String userWhere,

????????? ???????????String[] whereArgs) {

?? int count;

?? int match= URI_MATCHER.match(uri);

??DatabaseHelper database = getDatabaseForUri(uri);

?? //找到對(duì)應(yīng)的數(shù)據(jù)庫對(duì)象

??SQLiteDatabase db = database.getWritableDatabase();

?? ......

? ?synchronized (sGetTableAndWhereParam) {

????getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);

???? switch(match) {

????? ???......

???? ???case VIDEO_MEDIA:

???? ???case VIDEO_MEDIA_ID:{

??? ??????ContentValues values = newContentValues(initialValues);

??? ??????values.remove(ImageColumns.BUCKET_ID);

????? ????values.remove(ImageColumns.BUCKET_DISPLAY_NAME);

?????? ???......//調(diào)用SQLiteDatabase的update函數(shù)更新數(shù)據(jù)庫

?????? ???count =db.update(sGetTableAndWhereParam.table, values,

???????????????????????????????sGetTableAndWhereParam.where, whereArgs);

?????? ??.......

?? ??????}......//其他處理

???? }......//synchronized處理結(jié)束

?? ?if (count > 0 &&!db.inTransaction()) //調(diào)用notifyChange觸發(fā)通知

??????? getContext().getContentResolver().notifyChange(uri,null);

?? returncount;

}

由以上代碼可知,MediaProvider update函數(shù)更新完數(shù)據(jù)庫后,將通過notfiyChange函數(shù)來通知觀察者。notfiyChange函數(shù)的代碼如下:

[-->ContentResolver.java::notifyChange]

public void notifyChange(Uri uri, ContentObserverobserver) {

???? //在一般情況下,observer參數(shù)為null。調(diào)用另一個(gè)notifyChange函數(shù),直接來看它

????notifyChange(uri, observer, true);

?}

public void notifyChange(Uri uri, ContentObserverobserver,

????????????????????????? ?????boolean syncToNetwork) {

? //第三個(gè)參數(shù)syncToNetwork用于控制是否需要發(fā)起一次數(shù)據(jù)同步請(qǐng)求

? try {

?????? //調(diào)用ContentService的notifyChange函數(shù)

????? ?getContentService().notifyChange(

?????????????uri, observer == null ? null : observer.getContentObserver(),

?????????????observer != null && observer.deliverSelfNotifications(),

?????????????syncToNetwork);

??????? } ......

}

由以上代碼可知,ContentService的notifyChange函數(shù)將被調(diào)用,其代碼如下:

[-->ContentSerivce::notifyChange]

public void notifyChange(Uri uri, IContentObserverobserver,

?????boolean observerWantsSelfNotifications, boolean syncToNetwork) {

? longidentityToken = clearCallingIdentity();

? try {

?????????ArrayList<ObserverCall> calls = newArrayList<ObserverCall>();

????????? //從根節(jié)點(diǎn)開始搜索需要通知的觀察者,結(jié)果保存在calls數(shù)組中

?????????synchronized (mRootNode) {

???????????????mRootNode.collectObserversLocked(uri, 0, observer,

?????????????????????????????? observerWantsSelfNotifications,calls);

????????? }

????????final int numCalls = calls.size();

???????? for(int i=0; i<numCalls; i++) {

??????????????ObserverCall oc = calls.get(i);

??????????????try {

???????????????????/*

???????????????????? 調(diào)用客戶端IContentObserver Bn端,即ContentObserver

??? ?????????????????內(nèi)部類Transport的onChange函數(shù)。最后再由Transport調(diào)用

???????????????????? 客戶端提供的ContentObserver子類的onChange函數(shù)

??????????????????*/

???????????????????oc.mObserver.onChange(oc.mSelfNotify);

???????????????} ......//異常處理

???????????}

???????????if (syncToNetwork) {

???????????????SyncManager syncManager = getSyncManager();

???????????????if (syncManager != null) {

???????????????????//發(fā)起一次同步請(qǐng)求,相關(guān)內(nèi)容留待8.4節(jié)再分析

???????????????????syncManager.scheduleLocalSync(null,

????????????????????????????????????????? ?uri.getAuthority());

???????????????}

???????????}

??????? }finally {

???????????restoreCallingIdentity(identityToken);

??????? }

??? }

8.2.4? 數(shù)據(jù)更新通知機(jī)制總結(jié)和深入探討

總結(jié)上面所描述的數(shù)據(jù)更新通知機(jī)制的流程如圖8-2所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter8/image002.png" alt="image" />

圖8-2? 數(shù)據(jù)更新通知的流程圖

從前面的代碼介紹和圖8-2所示的流程來看,Android平臺(tái)中的數(shù)據(jù)更新通知機(jī)制還較為簡(jiǎn)單。不過此處尚有幾個(gè)問題想和讀者一起探討。

問題一:由圖8-2可知,客戶端2調(diào)用ContentProvider的update函數(shù)將間接觸發(fā)客戶端1的ContentObserver的onChange函數(shù)被調(diào)用。如果客戶端1在onChange函數(shù)中耗時(shí)過長(zhǎng),會(huì)不會(huì)導(dǎo)致客戶端2阻塞在update函數(shù)中呢?

想到這個(gè)問題的讀者應(yīng)該是非常細(xì)致和認(rèn)真的了。確實(shí),從前面所示的代碼和流程圖來看,這個(gè)情況幾乎是必然會(huì)發(fā)生的,但是實(shí)際上該問題并不存在,原因在于下面這一段代碼:

[-->IContentObserver.java::Proxy:onChange]

private static class Proxy implementsandroid.database.IContentObserver {

???? privateandroid.os.IBinder mRemote;

???? ......

???? publicvoid onChange(boolean selfUpdate)

????????? ????????????throws android.os.RemoteException {

??????? android.os.Parcel_data = android.os.Parcel.obtain();

??????? try{

?????????? _data.writeInterfaceToken(DESCRIPTOR);

?????????? _data.writeInt(((selfUpdate)? (1) : (0)));

??????????//調(diào)用客戶端1的ContentObserver Bn端的onChange函數(shù)

?????????? mRemote.transact(Stub.TRANSACTION_onChange,_data, null,

??????????????????????????? ????????????android.os.IBinder.FLAG_ONEWAY);

????? ??} finally {

??????????? _data.recycle();

???? ???}

???? }

???? ......

}

以上代碼告訴我們,ContentService在調(diào)用客戶端注冊(cè)的IContentObserver 的onChange函數(shù)時(shí),使用了FLAG_ONEWAY標(biāo)志。根據(jù)第2章對(duì)該標(biāo)志的介紹(參見2.2.1節(jié)),使用該標(biāo)志的Binder調(diào)用只需將請(qǐng)求發(fā)給binder驅(qū)動(dòng)即可,無需等待客戶端onChange函數(shù)的返回。因此,即使客戶端1在onChange中惡意浪費(fèi)時(shí)間,也不會(huì)阻塞客戶端2的update函數(shù)了。

問題二:這是一個(gè)開放性問題,最終需要讀者給出合適的答案。

假設(shè)服務(wù)端有一項(xiàng)功能,需要客戶端通過某種方式來控制它的開閉(即禁止或使用該功能),考慮一下有幾種方式來實(shí)現(xiàn)這個(gè)控制機(jī)制。

Android平臺(tái)上至少有三種方法可以實(shí)現(xiàn)這個(gè)控制機(jī)制。

第一種:服務(wù)端實(shí)現(xiàn)一個(gè)API函數(shù),客戶端直接調(diào)用這個(gè)函數(shù)來控制。

第二種客戶端發(fā)送指定的廣播,而服務(wù)端注冊(cè)該廣播的接收者,然后在這個(gè)廣播接收者的onReceive函數(shù)中去處理。

第三種:服務(wù)端輸出一個(gè)ContentProvider,并為這個(gè)功能輸出一個(gè)uri地址,然后注冊(cè)一個(gè)ContentObserver。客戶端可通過更新數(shù)據(jù)的方式來觸發(fā)服務(wù)端ContentObserver的onChange函數(shù),服務(wù)端在該函數(shù)中做對(duì)應(yīng)處理即可。

在Android代碼中,這三種方法都有地方使用。下面將以Settings應(yīng)用中和USB相關(guān)的功能設(shè)置為例來觀察第一種和第三種方法的使用情況。

第一個(gè)實(shí)例和Android 4.0中新支持的USB MTP/PTP功能有關(guān),相關(guān)代碼如下:

[-->UsbSettings.java::onPreferenceTreeClick]

public booleanonPreferenceTreeClick(PreferenceScreen preferenceScreen,

??????????????????????????? ?Preference preference) {

?

?? ......

?? if(preference == mMtp) {

????????mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP,true);

???????updateToggles(UsbManager.USB_FUNCTION_MTP);

?? } else if(preference == mPtp) {

????????mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_PTP, true);

??????? ?updateToggles(UsbManager.USB_FUNCTION_PTP);

?? }

?? returntrue;

?}

由以上代碼可知,如果用戶從Settings界面中選擇了使能MTP,將直接調(diào)用UsbManager的setCurrentFunction來使能MTP功能。這個(gè)函數(shù)的Bn端實(shí)現(xiàn)在UsbService中。

不過,同樣是USB相關(guān)的功能控制,ADB的開關(guān)控制卻采用了第三種方法,相關(guān)代碼為:

[-->DevelopmentSettings.java::onClick]

public void onClick(DialogInterface dialog, intwhich) {

??if (which == DialogInterface.BUTTON_POSITIVE){

???????mOkClicked = true;

?????? //設(shè)置Settings數(shù)據(jù)庫ADB對(duì)應(yīng)的數(shù)據(jù)項(xiàng)值為1

???????Settings.Secure.putInt(getActivity().getContentResolver(),

???????????????????Settings.Secure.ADB_ENABLED, 1);

?? } else

???????mEnableAdb.setChecked(false);//界面更新

?}

上面的數(shù)據(jù)項(xiàng)更新操作將導(dǎo)致UsbDeviceManager做對(duì)應(yīng)處理,其相關(guān)代碼如下:

[-->UsbDeviceManager.java::onChange]

private class AdbSettingsObserver extendsContentObserver {

?? ......

?? publicvoid onChange(boolean selfChange) {

?????? ?//從數(shù)據(jù)庫中取出對(duì)應(yīng)項(xiàng)的值

?????? ?boolean enable =(Settings.Secure.getInt(mContentResolver,

????????????????????????????Settings.Secure.ADB_ENABLED, 0) > 0);

??????????//發(fā)送MSG_ENABLE_ADB消息,UsbDeviceManager將處理此消息

????????? ?mHandler.sendMessage(MSG_ENABLE_ADB, enable);

???? }

}

同樣是USB相關(guān)的功能,Settings應(yīng)用卻采用了兩種截然不同的方法來處理它們。這種做法為筆者目前所從事的項(xiàng)目中USB擴(kuò)展功能的實(shí)現(xiàn)帶來了極大困擾,因?yàn)槲覀兿氩捎媒y(tǒng)一的方法來處理USB相關(guān)功能。到底應(yīng)采用哪種方法比較合適呢?第一種方法和第三種方法各自的適用場(chǎng)景是什么?讀者不妨仔細(xì)思考并將結(jié)論告訴筆者。

問題三:我們?cè)诘?章中分析Cursorquery時(shí)曾看到過ContentObserver的身影,但是并沒有對(duì)其進(jìn)行詳細(xì)分它。如果現(xiàn)在回過頭去分析query流程中和ContentObserver相關(guān)的部分,所涉及的流程可能比本節(jié)內(nèi)容還要多。

8.3? AccountManagerService分析

本節(jié)將分析AccountManagerService。如前所述,AccountManagerService負(fù)責(zé)管理手機(jī)中用戶的online賬戶,主要工作涉及賬戶的添加和刪除、AuthToken(全稱為authentication token。有了它,客戶端就無須每次操作都向服務(wù)器發(fā)送密碼了)的獲取和更新等。關(guān)于AccountManagerSerivce更詳細(xì)的功能,可閱讀SDK文檔中AccountManager的說明。

下面看AccountManagerService創(chuàng)建時(shí)的代碼:

8.3.1? 初識(shí)AccountManagerService

[-->SystemServer.java::ServerThread.run]

? ......

??//注冊(cè)AccountManagerService到ServiceManager,服務(wù)名為“account”

??ServiceManager.addService(Context.ACCOUNT_SERVICE,

????????????????????????????????????newAccountManagerService(context));

其構(gòu)造函數(shù)的代碼如下:

[-->AccountManagerService.java::AccountManagerService]

public AccountManagerService(Context context) {

?? //調(diào)用另外一個(gè)構(gòu)造函數(shù),其第三個(gè)參數(shù)將構(gòu)造一個(gè)AccountAuthenticatorCache對(duì)象,它是

?? //什么呢?見下文分析

???this(context,context.getPackageManager(),

????????????new? AccountAuthenticatorCache(context));

}

在AccountManagerService構(gòu)造函數(shù)中創(chuàng)建了一個(gè)AccountAuthenticatorCache對(duì)象,它是什么?來看下文。

1.? AccountAuthenticatorCache分析

AccountAuthenticatorCache是Android平臺(tái)中賬戶驗(yàn)證服務(wù)(Account AuthenticatorService,AAS)的管理中心。而AAS則由應(yīng)用程序通過在AndroidManifest.xml中輸出符合指定要求的Service信息而來。稍后讀者將看到這些要求的具體格式。

先來看AccountAuthenticatorCache的派生關(guān)系,如圖8-3所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter8/image003.png" alt="image" />

圖8-3? AccountAuthenticatorCache類圖

由圖8-3可知:

·??AccountAuthenticatorCache從RegisteredServicesCache<AuthenticatorDescription>派生。RegisteredServicesCache是一個(gè)模板類,專門用于管理系統(tǒng)中指定Service的信息收集和更新,而具體是哪些Service由RegisteredServicesCache構(gòu)造時(shí)的參數(shù)指定。AccountAuthenticatorCache對(duì)外輸出由RegisteredServicesCache模板參數(shù)指定的類的實(shí)例。在圖8-3中應(yīng)該就是AuthenticatorDescription。

·??AuthenticatorDescription繼承了Parcelable接口,這代表它可以跨Binder傳遞。該類描述了AAS相關(guān)的信息。

·??AccountAuthenticatorCache實(shí)現(xiàn)了IAccountAuthenticatorCache接口。這個(gè)接口供外部調(diào)用者使用以獲取AAS的信息。

下面看AccountAuthenticatorCache的創(chuàng)建,其相關(guān)代碼如下:

[-->AccountAuthenticatorCache.java::AccountAuthenticatorCache]

public AccountAuthenticatorCache(Context context){

? /*

? ACTION_AUTHENTICATOR_INTENT值為"android.accounts.AccountAuthenticator"

? AUTHENTICATOR_META_DATA_NAME值為"android.accounts.AccountAuthenticator"

? AUTHENTICATOR_ATTRIBUTES_NAME值為"account-authenticator"

? */

??super(context,

?????????? AccountManager.ACTION_AUTHENTICATOR_INTENT,

??????????AccountManager.AUTHENTICATOR_META_DATA_NAME,

??????????AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer);

?}

AccountAuthenticatorCache調(diào)用在其基類RegisteredServicesCache的構(gòu)造函數(shù)時(shí),傳遞了3個(gè)字符串參數(shù),這3個(gè)參數(shù)用于控制RegisteredServicesCache從PackageManagerService獲取哪些Service的信息。

(1)?RegisteredServicesCache分析

[-->RegisteredServicesCache.java::RegisteredServicesCache]

public RegisteredServicesCache(Context context,String interfaceName,

???????????????????????????????????? StringmetaDataName, String attributeName,

????????????????????????????XmlSerializerAndParser<V>serializerAndParser) {

??? mContext= context;

?? ?//保存?zhèn)鬟f進(jìn)來的參數(shù)

???mInterfaceName = interfaceName;

???mMetaDataName = metaDataName;

???mAttributesName = attributeName;

???mSerializerAndParser = serializerAndParser;

?

??? FiledataDir = Environment.getDataDirectory();

??? FilesystemDir = new File(dataDir, "system");

???//syncDir指向/data/system/registered_service目錄

??? FilesyncDir = new File(systemDir, "registered_services");

??? //下面這個(gè)文件指向syncDir目錄下的android.accounts.AccountAuthenticator.xml

???mPersistentServicesFile = new AtomicFile(new File(syncDir,

????????????????????????????????????????? interfaceName+ ".xml"));

??? //生成服務(wù)信息

???generateServicesMap();

?

??? finalBroadcastReceiver receiver = new BroadcastReceiver() {

??????public void onReceive(Context context1, Intent intent) {

?????????generateServicesMap();

???????? }

??? };

??? //注冊(cè)Package安裝、卸載和更新等廣播監(jiān)聽者

???mReceiver = new AtomicReference<BroadcastReceiver>(receiver);

?? ?IntentFilter intentFilter = newIntentFilter();

???intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);

???intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);

???intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);

???intentFilter.addDataScheme("package");

???mContext.registerReceiver(receiver, intentFilter);

?? ?IntentFilter sdFilter = new IntentFilter();

???sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);

???sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);

???mContext.registerReceiver(receiver, sdFilter);

}

由以上代碼可知:

·??成員變量mPersistentServicesFile指向/data/system/registered_service/目錄下的一個(gè)文件,該文件保存了以前獲取的對(duì)應(yīng)Service的信息。就AccountAuthenticator而言,mPersistentServicesFile指向該目錄的android.accounts.AccountAuthenticator.xml文件。

·??由于RegisteredServicesCache管理的是系統(tǒng)中指定Service的信息,當(dāng)系統(tǒng)中有Package安裝、卸載或更新時(shí),RegisteredServicesCache也需要對(duì)應(yīng)更新自己的信息,因?yàn)橛行㏒ervice可能會(huì)隨著APK被刪除而不復(fù)存在。

generateServiceMap函數(shù)將獲取指定的Service信息,其代碼如下:

[-->RegisteredServicesCache.java::generateServicesMap]

void generateServicesMap() {

?? //獲取PackageManager接口,用來和PackageManagerService交互

??PackageManager pm = mContext.getPackageManager();

??ArrayList<ServiceInfo<V>> serviceInfos = newArrayList<ServiceInfo<V>>();

?? /*

?? 在本例中,查詢PKMS中滿足Intent Action為"android.accounts.AccountAuthenticator"

?? 的服務(wù)信息。由以下代碼可知,這些信息指的是Service中聲明的MetaData信息

?? */

??List<ResolveInfo> resolveInfos = pm.queryIntentServices(

???????????????new Intent(mInterfaceName),PackageManager.GET_META_DATA);

?

?? for(ResolveInfo resolveInfo : resolveInfos) {

????? try {

??????????/*

??????????? 調(diào)用parserServiceInfo函數(shù)解析從PKMS中獲得的MetaData信息,該函數(shù)

?????????? ?返回的是一個(gè)模板類對(duì)象。就本例而言,這個(gè)函數(shù)返回一個(gè)

??????????? ServiceInfo<AccountAuthenticator>類型的對(duì)象

??????????*/

??????????ServiceInfo<V> info = parseServiceInfo(resolveInfo);

??????????serviceInfos.add(info);

???????? }

???? ?}

?

???synchronized (mServicesLock) {

?????? if(mPersistentServices == null)

???????????readPersistentServicesLocked();

??????mServices = Maps.newHashMap();

??????StringBuilder changes = new StringBuilder();

?????? ......//檢查mPersistentServices保存的服務(wù)信息和當(dāng)前從PKMS中取出來的PKMS

????? //信息,判斷是否有變化,如果有變化,需要通知監(jiān)聽者。讀者可自行閱讀這段代碼,

???? //注意其中uid的作用

???????mPersistentServicesFileDidNotExist = false;

???? }

?}

接下來解析Service的parseServiceInfo函數(shù)。

(2)?parseServiceInfo函數(shù)分析

[-->RegisteredServicesCache.java::parseServiceInfo]

private ServiceInfo<V>parseServiceInfo(ResolveInfo service)

?????????? ??????????throws XmlPullParserException, IOException {

??android.content.pm.ServiceInfo si = service.serviceInfo;

??ComponentName componentName = new ComponentName(si.packageName, si.name);

?

??PackageManager pm = mContext.getPackageManager();

?

??XmlResourceParser parser = null;

?? try {

??????? //解析MetaData信息

???????parser = si.loadXmlMetaData(pm, mMetaDataName);

???????AttributeSet attrs = Xml.asAttributeSet(parser);

?

??????? inttype;

???????......

?

????? StringnodeName = parser.getName();

????? //調(diào)用子類實(shí)現(xiàn)的parseServiceAttributes得到一個(gè)真實(shí)的對(duì)象,在本例中它是

????? //AuthenticatorDescription。注意,傳遞給parseServiceAttributes的第一個(gè)

????? //參數(shù)代表MetaData中的resource信息。詳細(xì)內(nèi)容見下文的圖例

???? V v=parseServiceAttributes(

???????????????????? pm.getResourcesForApplication(si.applicationInfo),

????????????????????si.packageName, attrs);

?

??? finalandroid.content.pm.ServiceInfo serviceInfo = service.serviceInfo;

??? finalApplicationInfo applicationInfo = serviceInfo.applicationInfo;

??? finalint uid = applicationInfo.uid;

??? returnnew ServiceInfo<V>(v, componentName, uid);

? ?} ...... finally {

???????????if (parser != null) parser.close();

??? }

?}

parseServiceInfo將解析Service中的MetaData信息,然后調(diào)用子類實(shí)現(xiàn)的parseServiceAttributes函數(shù),以獲取特定類型Service的信息。

下面通過實(shí)例向讀者展示最終的解析結(jié)果。

(3)?AccountAuthenticatorCache分析總結(jié)

在Email應(yīng)用的AndroidManifest.xml中定義了一個(gè)AAS,如圖8-4所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter8/image004.png" alt="image" />

圖8-4? Email AAS定義

由圖8-4可知,在Email中這個(gè)Service對(duì)應(yīng)為EasAuthenticatorService,其Intent匹配的Action為“android.accounts.AccountAuthenticator”,其MetaData的name為“android.accounts.AccountAuthenticator”,而MetaData的具體信息保存在resource資源中,在本例中,它指向另外一個(gè)xml文件,即eas_authenticator.xml,此文件的內(nèi)容如圖8-5所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter8/image005.png" alt="image" />

圖8-5? eas_authenticator.xml的內(nèi)容

圖8-5為Email中eas_authenticator.xml的內(nèi)容。這個(gè)xml中的內(nèi)容是有嚴(yán)格要求的,其中:

·??accountType標(biāo)簽用于指定賬戶類型(賬戶類型和具體應(yīng)用有關(guān),Android并未規(guī)定賬戶的類型)。

·??icon、smallIcon、label和accountPreferences等用于界面顯示。例如,當(dāng)需要用戶輸入賬戶信息時(shí),系統(tǒng)會(huì)彈出一個(gè)Activity,上述幾個(gè)標(biāo)簽就用于界面顯示。詳細(xì)情況可閱讀SDK文檔AbstractAccountAuthenticator的說明。

而android.accounts.AccountAuthenticator.xml的內(nèi)容如圖8-6所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter8/image006.png" alt="image" />

圖8-6? android.accounts.AccountAuthenticator.xml的內(nèi)容

由圖8-6可知,筆者的測(cè)試機(jī)器上有3個(gè)AAS服務(wù),其中同一個(gè)uid有兩個(gè)服務(wù)(即uid為10015對(duì)應(yīng)的兩個(gè)Service)。

提示uid是在為PackageManagerService解析APK文件時(shí)賦予APK的。讀者不妨自行閱讀frameworks/base/services/java/com/android/server/pm/Settings.java中的newUserIdLPw函數(shù)。

下面來看AccountManagerService的構(gòu)造函數(shù)。

2.?AccountManagerService構(gòu)造函數(shù)分析

[-->AccountManagerService.java::AccountManagerService]

public AccountManagerService(Context context,PackageManager packageManager,

???????????IAccountAuthenticatorCache authenticatorCache) {

?

??? mContext= context;

???mPackageManager = packageManager;

?

??synchronized (mCacheLock) {

????? //此數(shù)據(jù)庫文件對(duì)應(yīng)為/data/system/accounts.db

?????mOpenHelper = new DatabaseHelper(mContext);

?? }

?

??mMessageThread = new HandlerThread("AccountManagerService");

??mMessageThread.start();

??mMessageHandler = new MessageHandler(mMessageThread.getLooper());

?

??mAuthenticatorCache = authenticatorCache;

?? //為AccountAuthenticatorCache設(shè)置一個(gè)監(jiān)聽者,一旦AAS服務(wù)發(fā)生變化,

??//AccountManagerService需要做對(duì)應(yīng)處理

??mAuthenticatorCache.setListener(this, null /* Handler */);

?

??sThis.set(this);

?? //監(jiān)聽ACTION_PACKAGE_REMOVED廣播

??IntentFilter intentFilter = new IntentFilter();

??intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);

??intentFilter.addDataScheme("package");

??mContext.registerReceiver(new BroadcastReceiver() {

?? publicvoid onReceive(Context context1, Intent intent) {

???????purgeOldGrants();

???? }

?? },intentFilter);

? ?/*

??? accounts.db數(shù)據(jù)庫中有一個(gè)grants表,用于存儲(chǔ)授權(quán)信息,該信息用于保存哪些Package

?? ?有權(quán)限獲取賬戶信息。下面的函數(shù)將根據(jù)grants表中的數(shù)據(jù)查詢PKMS,以判斷這些

?? ?Package是否還存在。如果系統(tǒng)中已經(jīng)不存在這些Package,則grants表需要更新

? */

? ?purgeOldGrants();

?? /*

??accounts.db中有一個(gè)accounts表,該表中存儲(chǔ)了賬戶類型和賬戶名。其中,賬戶類型

?? 就是AuthenticatorDescription中的accountType,它和具體應(yīng)用有關(guān)。下面這個(gè)

?? 函數(shù)將比較accounts表中的內(nèi)容與AccountAuthenticatorCache中服務(wù)的信息,如果

?? AccountAuthenticatorCache已經(jīng)不存在對(duì)應(yīng)賬戶類型的服務(wù),則需要?jiǎng)h除accounts表

?? 中的對(duì)應(yīng)項(xiàng)

? */

??validateAccountsAndPopulateCache();

?}

AccountManagerService的構(gòu)造函數(shù)較簡(jiǎn)單,有興趣的讀者可自行研究以上代碼中未詳細(xì)分析的函數(shù)。下面將通過一個(gè)具體的例子來分析AccountManagerService的工作流程。

8.3.2? AccountManageraddAccount分析

這一節(jié)將分析AccountManagerService中的一個(gè)重要的函數(shù),即addAccount,其功能是為某項(xiàng)賬戶添加一個(gè)用戶。下面以前面提及的Email為例來認(rèn)識(shí)AAS的處理流程。

AccountManagerService是一個(gè)運(yùn)行在SystemServer中的服務(wù),客戶端進(jìn)程必須借助AccountManager提供的API來使用AccountManagerService服務(wù),所以,本例需從AccountManager的addAccount函數(shù)講起。

1.? AccountManager的addAccount發(fā)起請(qǐng)求

AccountManager 的addAccount函數(shù)的參數(shù)和返回值較復(fù)雜,先看其函數(shù)原型:

public AccountManagerFuture<Bundle>addAccount(

?? finalString accountType,

?? finalString authTokenType,

?? finalString[] requiredFeatures,

?? finalBundle addAccountOptions,

?? finalActivity activity,

?? AccountManagerCallback<Bundle>callback,

?? Handlerhandler)

在以上代碼中:

·??addAccount的返回值類型是AccountManagerFuture<Bundle>。其中,AccountManagerFuture是一個(gè)模板Interface,其真實(shí)類型只有在分析addAccount的實(shí)現(xiàn)時(shí)才能知道?,F(xiàn)在可以告訴讀者的是,它和Java并發(fā)庫(concurrent庫)中的FutureTask有關(guān),是對(duì)異步函數(shù)調(diào)用的一種封裝[①]。調(diào)用者在后期只要調(diào)用它的getResult函數(shù)即可取得addAccount的調(diào)用結(jié)果。由于addAccount可能涉及網(wǎng)絡(luò)操作(例如,AAS需要把賬戶添加到網(wǎng)絡(luò)服務(wù)器上),所以這里采用了異步調(diào)用的方法以避免長(zhǎng)時(shí)間的阻塞。這也是AccountManagerFuture的getResult不能在主線程中調(diào)用的原因。

·??addAccount的第一個(gè)參數(shù)accountType代表賬戶類型。該參數(shù)不能為空。就本例而言,它的值為“com.android.email”。

·??authTokenType、requiredFeatures和addAccountOptions與具體的AAS服務(wù)有關(guān)。如果想添加指定賬戶類型的Account,則須對(duì)其背后的AAS有所了解。

·??activity:此參數(shù)和界面有關(guān)。例如有些AAS需要用戶輸入用戶名和密碼,故需啟動(dòng)一Activity。在這種情況下,AAS會(huì)返回一個(gè)Intent,客戶端將通過這個(gè)activity啟動(dòng)Intent所標(biāo)示的Activity。讀者將通過下文的分析了解這一點(diǎn)。

·??callback和handler:這兩個(gè)參數(shù)與如何獲取addAccount返回結(jié)果有關(guān)。如這兩個(gè)參數(shù)為空,客戶端則須單獨(dú)啟動(dòng)一個(gè)線程去調(diào)用AccountManagerFuture的getResult函數(shù)。

addAccount的代碼如下:

[-->AccountManager.java::addAccount]

public AccountManagerFuture<Bundle>addAccount(final String accountType,

???? finalString authTokenType, final String[] requiredFeatures,

???? finalBundle addAccountOptions, final Activity activity,

????AccountManagerCallback<Bundle> callback, Handler handler) {

??? if(accountType == null) //accountType不能為null

??????????? thrownew IllegalArgumentException("accountType is null");

?? finalBundle optionsIn = new Bundle();

?? if(addAccountOptions != null)//保存客戶端傳入的addAccountOptions

??????optionsIn.putAll(addAccountOptions);

?

??optionsIn.putString(KEY_ANDROID_PACKAGE_NAME,

??????????????????????? mContext.getPackageName());

??? //構(gòu)造一個(gè)匿名類對(duì)象,該類繼承自AmsTask,并實(shí)現(xiàn)了doWork函數(shù)。addAccount返回前

??? //將調(diào)用該對(duì)象的start函數(shù)

??? returnnew AmsTask(activity, handler, callback) {

????? publicvoid doWork() throws RemoteException {

???????????//mService用于和AccountManagerService通信

???????????mService.addAcount(mRespo