[深入理解Android卷二 全文-第八章]深入理解ContentService和AccountManagerService

由于《深入理解Android 卷一》和《深入理解Android卷二》不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容

第8章  深入理解ContentService和AccountManagerService

本章主要内容:

·  介绍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包含以下两个主要功能:

·  它是Android平台中数据更新通知的执行者。数据更新通知与第7章分析Cursorquery函数实现时提到的ContentObserver有关。这部分内容将在8.2节中分析。

·  它是Android平台中数据同步服务的管理中枢。当用户通过Android手机中的Contacts应用将联系人信息同步到远端服务器时,就需要和ContentService交互。这部分内容是本章的难点,将在8.4节中进行分析

本章要分析的第二个Service是AccountManagerService,它负责管理Android手机中用户的账户,这些账户是用户的online账户,例如用户在Google、Facebook上注册的账户。

本章将先分析ContentService中数据通知机制的实现,然后分析AccountManagerService,最后再介绍ContentService中的数据同步服务。

提示这两个Service的难度都不大,它们在设计结构上有较大的相似性,在内容上也有一定的关联。另外,作为本书最后一章,笔者照例会留一些难度适中的问题或知识点供读者自行分析研究。

8.2  数据更新通知机制分析

何为数据更新通知?先来看日常生活中的一个例子。

笔者所在公司采用BugZilla来管理Bug。在日常工作中,笔者和同事们的一部分工作就是登录BugZilla查询各自名下的Bug并修改它们。如何跟踪自己的Bug呢?其实,以上描述中已经提到了一种方法,即登录BugZilla并查询。除此之外,BugZilla还支持另一种方法,即为每个Bug设置一个关系人列表,一旦该Bug的状态发生变化,BugZilla就会给该Bug关系人列表中的人发送邮件。

上例中提到的第二种方法就是本节要分析的数据更新通知机制。一般说来,领导和项目经理(PM)使用第一种方法居多,因为他们需要不定时地查询和统计全局Bug的情况。而程序员使用第二种方法较多(也许是没办法的事情吧,谁会情愿主动查询自己的Bug呢?)。

类似的通知机制在日常生活中的其他地方还有使用。在操作系统中,这种通知机制同样也广泛存在。例如,在OS中,设计人员一般会安排外部设备以中断的方式通知CPU并让其开展后续处理,而不会让CPU去轮询外设的状态。

现在回到Android平台,如果程序需要监控某数据项的变化,可以采用一个类似while循环的语句不断查询它以判断其值是否发生了变化。显而易见,这种方式的效率很低。有了通知机制以后,程序只需注册一个ContentObserver实例即可。一旦该项数据发生变化,系统就会通过ContentObserver的onChange函数来通知我们。与前面所述的轮询相比,此处的通知方式明显更高效。

通过上面的描述可以知道,通知机制的实施包括两个步骤:第一步,注册观察者;第二步,通知观察者。在Android平台中,这两步都离不开ContentService,下面来认识一下它。

提示在设计模式中,通知机制对应的模式是Observer模式,即观察者模式。

8.2.1  初识ContentService

SystemServer创建ContentService的代码非常简单,如下所示:

[-->SystemServer::ServerThread.run]

public void run() {

......

ContentService.main(context,

factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL);

......

}

以上代码中直接调用了ContentService的main函数。在一般情况下,该函数第二个参数为false。此main函数的代码如下:

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

public static IContentService main(Contextcontext, boolean factoryTest) {

//构造ContentService实例

ContentService service = new ContentService(context, factoryTest);

//将ContentService注册到ServiceManager中,其注册名叫“content”

ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME,

service);

returnservice;

}

ContentService的构造函数的代码如下:

[-->ContentService.java::ContentService]

ContentService(Context context, booleanfactoryTest) {

mContext = context;

mFactoryTest = factoryTest;

getSyncManager(); //获取同步服务管理对象,接下来看它的代码

}

[-->ContentService.java::getSyncManager]

private SyncManager getSyncManager() {

synchronized(mSyncManagerLock) {

try {

//创建一个SyncManager实例,它是ContentService中负责数据同步服务的

//主力成员。留待8.4节再详细分析它

if (mSyncManager == null) mSyncManager = new

SyncManager(mContext,mFactoryTest);

}......

return mSyncManager;

}

}

看完以上代码读者可能会觉得ContentService比较简单。其实,ContentService中最难的功能是数据同步服务,不过该功能的实现都封装在SyncManager及相关类中了,所以在分析通知机制时不会和数据同步服务有太多瓜葛。

下面来分析通知机制实施的第一步,注册ContentObserver。该步骤由ContentResovler提供的registerContentObserver函数来实现。

8.2.2  ContentResovler的registerContentObserver分析

[-->ContentResolver.java::registerContentObserver]

public final void registerContentObserver(Uri uri,

booleannotifyForDescendents,ContentObserver observer){

/*

注意registerContentObserver传递的参数,其中:

uri是客户端设置的它所需要监听的数据项的地址,用Uri来表示

notifyForDescendents:如果该值为true,则所有地址包含此uri的数据项发生变化时

都会触发通知。否则只有完全符合该uri地址的数据项发生变化时才会触发通知。以文件夹和

其中的文件为例,若uri指向某文件夹,则需设置notifyForDescendents为true。即该文件

夹下的任何文件发生变化,都需要通知监听者。

observer是客户端设置的监听对象。当数据项发生变化时,该对象的onChange函数将被调用

*/

try {

/*

调用ContentService的registerContentObserver函数,其第三个参数是

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

*/

getContentService().registerContentObserver(uri,

notifyForDescendents,

observer.getContentObserver());

} ......

}

registerContentObserver最终将调用ContentService的registerContentObserver函数,其中第三个参数是ContentObservergetContentObserver的返回值。这个返回值是什么呢?需请出ContentObserver家族成员。

1.  ContentObserver介绍

ContentObserver家族成员如图8-1所示。

图8-1  ContentObserver家族类图

图8-1中的ContentObserver类和第7章中介绍的ContentProvider类非常类似,内部都定义了一个Transport类参与Binder通信。由图8-1可知,Transport类从IContentObserver.stub派生。从Binder通信角度来看,客户端进程中的Transport将是Bn端。如此,通过registerContentObserver传递到ContentService所在进程的就是Bp端。IContentObserverBp端对象的真实类型是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函数分析

下面来看ContentService的registerContentObserver函数的代码。

[-->ContentService.java::registerContentObserver]

public void registerContentObserver(Uri uri,boolean notifyForDescendents,

IContentObserver observer) {

......

synchronized (mRootNode) {

//ContentService要做的事情其实很简单,就是保存uri和observer的对应关系到

//其内部变量mRootNode中

mRootNode.addObserverLocked(uri, observer, notifyForDescendents,

mRootNode, Binder.getCallingUid(),

Binder.getCallingPid());

}

mRootNode是ContentService的成员变量,其类型为ObserverNode。ObserverNode的组织形式是数据结构中的树,其叶子节点的类型为ObserverEntry,它保存了uri和对应的IContentObserver对象。本节不关注它们的内部实现,读者若有兴趣,不妨自行研究。

至此,客户端已经为某数据项设置了ContentObserver。再来看通知机制实施的第二步,即通知观察者。

8.2.3  ContentResolver的 notifyChange分析

数据更新的通知由ContentResolver的notifyChange函数触发。看MediaProvider的update函数的代码如下:

[-->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);

//找到对应的数据库对象

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);

......//调用SQLiteDatabase的update函数更新数据库

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

sGetTableAndWhereParam.where, whereArgs);

.......

}......//其他处理

}......//synchronized处理结束

if (count > 0 &&!db.inTransaction()) //调用notifyChange触发通知

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

returncount;

}

由以上代码可知,MediaProvider update函数更新完数据库后,将通过notfiyChange函数来通知观察者。notfiyChange函数的代码如下:

[-->ContentResolver.java::notifyChange]

public void notifyChange(Uri uri, ContentObserverobserver) {

//在一般情况下,observer参数为null。调用另一个notifyChange函数,直接来看它

notifyChange(uri, observer, true);

}

public void notifyChange(Uri uri, ContentObserverobserver,

boolean syncToNetwork) {

//第三个参数syncToNetwork用于控制是否需要发起一次数据同步请求

try {

//调用ContentService的notifyChange函数

getContentService().notifyChange(

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

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

syncToNetwork);

} ......

}

由以上代码可知,ContentService的notifyChange函数将被调用,其代码如下:

[-->ContentSerivce::notifyChange]

public void notifyChange(Uri uri, IContentObserverobserver,

boolean observerWantsSelfNotifications, boolean syncToNetwork) {

longidentityToken = clearCallingIdentity();

try {

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

//从根节点开始搜索需要通知的观察者,结果保存在calls数组中

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 {

/*

调用客户端IContentObserver Bn端,即ContentObserver

内部类Transport的onChange函数。最后再由Transport调用

客户端提供的ContentObserver子类的onChange函数

*/

oc.mObserver.onChange(oc.mSelfNotify);

} ......//异常处理

}

if (syncToNetwork) {

SyncManager syncManager = getSyncManager();

if (syncManager != null) {

//发起一次同步请求,相关内容留待8.4节再分析

syncManager.scheduleLocalSync(null,

uri.getAuthority());

}

}

}finally {

restoreCallingIdentity(identityToken);

}

}

8.2.4  数据更新通知机制总结和深入探讨

总结上面所描述的数据更新通知机制的流程如图8-2所示。

图8-2  数据更新通知的流程图

从前面的代码介绍和图8-2所示的流程来看,Android平台中的数据更新通知机制还较为简单。不过此处尚有几个问题想和读者一起探讨。

问题一:由图8-2可知,客户端2调用ContentProvider的update函数将间接触发客户端1的ContentObserver的onChange函数被调用。如果客户端1在onChange函数中耗时过长,会不会导致客户端2阻塞在update函数中呢?

想到这个问题的读者应该是非常细致和认真的了。确实,从前面所示的代码和流程图来看,这个情况几乎是必然会发生的,但是实际上该问题并不存在,原因在于下面这一段代码:

[-->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)));

//调用客户端1的ContentObserver Bn端的onChange函数

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

android.os.IBinder.FLAG_ONEWAY);

} finally {

_data.recycle();

}

}

......

}

以上代码告诉我们,ContentService在调用客户端注册的IContentObserver 的onChange函数时,使用了FLAG_ONEWAY标志。根据第2章对该标志的介绍(参见2.2.1节),使用该标志的Binder调用只需将请求发给binder驱动即可,无需等待客户端onChange函数的返回。因此,即使客户端1在onChange中恶意浪费时间,也不会阻塞客户端2的update函数了。

问题二:这是一个开放性问题,最终需要读者给出合适的答案。

假设服务端有一项功能,需要客户端通过某种方式来控制它的开闭(即禁止或使用该功能),考虑一下有几种方式来实现这个控制机制。

Android平台上至少有三种方法可以实现这个控制机制。

第一种:服务端实现一个API函数,客户端直接调用这个函数来控制。

第二种客户端发送指定的广播,而服务端注册该广播的接收者,然后在这个广播接收者的onReceive函数中去处理。

第三种:服务端输出一个ContentProvider,并为这个功能输出一个uri地址,然后注册一个ContentObserver。客户端可通过更新数据的方式来触发服务端ContentObserver的onChange函数,服务端在该函数中做对应处理即可。

在Android代码中,这三种方法都有地方使用。下面将以Settings应用中和USB相关的功能设置为例来观察第一种和第三种方法的使用情况。

第一个实例和Android 4.0中新支持的USB MTP/PTP功能有关,相关代码如下:

[-->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,将直接调用UsbManager的setCurrentFunction来使能MTP功能。这个函数的Bn端实现在UsbService中。

不过,同样是USB相关的功能控制,ADB的开关控制却采用了第三种方法,相关代码为:

[-->DevelopmentSettings.java::onClick]

public void onClick(DialogInterface dialog, intwhich) {

if (which == DialogInterface.BUTTON_POSITIVE){

mOkClicked = true;

//设置Settings数据库ADB对应的数据项值为1

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

Settings.Secure.ADB_ENABLED, 1);

} else

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

}

上面的数据项更新操作将导致UsbDeviceManager做对应处理,其相关代码如下:

[-->UsbDeviceManager.java::onChange]

private class AdbSettingsObserver extendsContentObserver {

......

publicvoid onChange(boolean selfChange) {

//从数据库中取出对应项的值

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

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

//发送MSG_ENABLE_ADB消息,UsbDeviceManager将处理此消息

mHandler.sendMessage(MSG_ENABLE_ADB, enable);

}

}

同样是USB相关的功能,Settings应用却采用了两种截然不同的方法来处理它们。这种做法为笔者目前所从事的项目中USB扩展功能的实现带来了极大困扰,因为我们想采用统一的方法来处理USB相关功能。到底应采用哪种方法比较合适呢?第一种方法和第三种方法各自的适用场景是什么?读者不妨仔细思考并将结论告诉笔者。

问题三:我们在第7章中分析Cursorquery时曾看到过ContentObserver的身影,但是并没有对其进行详细分它。如果现在回过头去分析query流程中和ContentObserver相关的部分,所涉及的流程可能比本节内容还要多。

8.3  AccountManagerService分析

本节将分析AccountManagerService。如前所述,AccountManagerService负责管理手机中用户的online账户,主要工作涉及账户的添加和删除、AuthToken(全称为authentication token。有了它,客户端就无须每次操作都向服务器发送密码了)的获取和更新等。关于AccountManagerSerivce更详细的功能,可阅读SDK文档中AccountManager的说明。

下面看AccountManagerService创建时的代码:

8.3.1  初识AccountManagerService

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

......

//注册AccountManagerService到ServiceManager,服务名为“account”

ServiceManager.addService(Context.ACCOUNT_SERVICE,

newAccountManagerService(context));

其构造函数的代码如下:

[-->AccountManagerService.java::AccountManagerService]

public AccountManagerService(Context context) {

//调用另外一个构造函数,其第三个参数将构造一个AccountAuthenticatorCache对象,它是

//什么呢?见下文分析

this(context,context.getPackageManager(),

new  AccountAuthenticatorCache(context));

}

在AccountManagerService构造函数中创建了一个AccountAuthenticatorCache对象,它是什么?来看下文。

1.  AccountAuthenticatorCache分析

AccountAuthenticatorCache是Android平台中账户验证服务(Account AuthenticatorService,AAS)的管理中心。而AAS则由应用程序通过在AndroidManifest.xml中输出符合指定要求的Service信息而来。稍后读者将看到这些要求的具体格式。

先来看AccountAuthenticatorCache的派生关系,如图8-3所示。

图8-3  AccountAuthenticatorCache类图

由图8-3可知:

·  AccountAuthenticatorCache从RegisteredServicesCache<AuthenticatorDescription>派生。RegisteredServicesCache是一个模板类,专门用于管理系统中指定Service的信息收集和更新,而具体是哪些Service由RegisteredServicesCache构造时的参数指定。AccountAuthenticatorCache对外输出由RegisteredServicesCache模板参数指定的类的实例。在图8-3中应该就是AuthenticatorDescription。

·  AuthenticatorDescription继承了Parcelable接口,这代表它可以跨Binder传递。该类描述了AAS相关的信息。

·  AccountAuthenticatorCache实现了IAccountAuthenticatorCache接口。这个接口供外部调用者使用以获取AAS的信息。

下面看AccountAuthenticatorCache的创建,其相关代码如下:

[-->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调用在其基类RegisteredServicesCache的构造函数时,传递了3个字符串参数,这3个参数用于控制RegisteredServicesCache从PackageManagerService获取哪些Service的信息。

(1) RegisteredServicesCache分析

[-->RegisteredServicesCache.java::RegisteredServicesCache]

public RegisteredServicesCache(Context context,String interfaceName,

StringmetaDataName, String attributeName,

XmlSerializerAndParser<V>serializerAndParser) {

mContext= context;

//保存传递进来的参数

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");

//下面这个文件指向syncDir目录下的android.accounts.AccountAuthenticator.xml

mPersistentServicesFile = new AtomicFile(new File(syncDir,

interfaceName+ ".xml"));

//生成服务信息

generateServicesMap();

finalBroadcastReceiver receiver = new BroadcastReceiver() {

public void onReceive(Context context1, Intent intent) {

generateServicesMap();

}

};

//注册Package安装、卸载和更新等广播监听者

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/目录下的一个文件,该文件保存了以前获取的对应Service的信息。就AccountAuthenticator而言,mPersistentServicesFile指向该目录的android.accounts.AccountAuthenticator.xml文件。

·  由于RegisteredServicesCache管理的是系统中指定Service的信息,当系统中有Package安装、卸载或更新时,RegisteredServicesCache也需要对应更新自己的信息,因为有些Service可能会随着APK被删除而不复存在。

generateServiceMap函数将获取指定的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"

的服务信息。由以下代码可知,这些信息指的是Service中声明的MetaData信息

*/

List<ResolveInfo> resolveInfos = pm.queryIntentServices(

new Intent(mInterfaceName),PackageManager.GET_META_DATA);

for(ResolveInfo resolveInfo : resolveInfos) {

try {

/*

调用parserServiceInfo函数解析从PKMS中获得的MetaData信息,该函数

返回的是一个模板类对象。就本例而言,这个函数返回一个

ServiceInfo<AccountAuthenticator>类型的对象

*/

ServiceInfo<V> info = parseServiceInfo(resolveInfo);

serviceInfos.add(info);

}

}

synchronized (mServicesLock) {

if(mPersistentServices == null)

readPersistentServicesLocked();

mServices = Maps.newHashMap();

StringBuilder changes = new StringBuilder();

......//检查mPersistentServices保存的服务信息和当前从PKMS中取出来的PKMS

//信息,判断是否有变化,如果有变化,需要通知监听者。读者可自行阅读这段代码,

//注意其中uid的作用

mPersistentServicesFileDidNotExist = false;

}

}

接下来解析Service的parseServiceInfo函数。

(2) parseServiceInfo函数分析

[-->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();

//调用子类实现的parseServiceAttributes得到一个真实的对象,在本例中它是

//AuthenticatorDescription。注意,传递给parseServiceAttributes的第一个

//参数代表MetaData中的resource信息。详细内容见下文的图例

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信息,然后调用子类实现的parseServiceAttributes函数,以获取特定类型Service的信息。

下面通过实例向读者展示最终的解析结果。

(3) AccountAuthenticatorCache分析总结

在Email应用的AndroidManifest.xml中定义了一个AAS,如图8-4所示。

图8-4  Email AAS定义

由图8-4可知,在Email中这个Service对应为EasAuthenticatorService,其Intent匹配的Action为“android.accounts.AccountAuthenticator”,其MetaData的name为“android.accounts.AccountAuthenticator”,而MetaData的具体信息保存在resource资源中,在本例中,它指向另外一个xml文件,即eas_authenticator.xml,此文件的内容如图8-5所示。

图8-5  eas_authenticator.xml的内容

图8-5为Email中eas_authenticator.xml的内容。这个xml中的内容是有严格要求的,其中:

·  accountType标签用于指定账户类型(账户类型和具体应用有关,Android并未规定账户的类型)。

·  icon、smallIcon、label和accountPreferences等用于界面显示。例如,当需要用户输入账户信息时,系统会弹出一个Activity,上述几个标签就用于界面显示。详细情况可阅读SDK文档AbstractAccountAuthenticator的说明。

而android.accounts.AccountAuthenticator.xml的内容如图8-6所示。

图8-6  android.accounts.AccountAuthenticator.xml的内容

由图8-6可知,笔者的测试机器上有3个AAS服务,其中同一个uid有两个服务(即uid为10015对应的两个Service)。

提示uid是在为PackageManagerService解析APK文件时赋予APK的。读者不妨自行阅读frameworks/base/services/java/com/android/server/pm/Settings.java中的newUserIdLPw函数。

下面来看AccountManagerService的构造函数。

2. AccountManagerService构造函数分析

[-->AccountManagerService.java::AccountManagerService]

public AccountManagerService(Context context,PackageManager packageManager,

IAccountAuthenticatorCache authenticatorCache) {

mContext= context;

mPackageManager = packageManager;

synchronized (mCacheLock) {

//此数据库文件对应为/data/system/accounts.db

mOpenHelper = new DatabaseHelper(mContext);

}

mMessageThread = new HandlerThread("AccountManagerService");

mMessageThread.start();

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

mAuthenticatorCache = authenticatorCache;

//为AccountAuthenticatorCache设置一个监听者,一旦AAS服务发生变化,

//AccountManagerService需要做对应处理

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

sThis.set(this);

//监听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数据库中有一个grants表,用于存储授权信息,该信息用于保存哪些Package

有权限获取账户信息。下面的函数将根据grants表中的数据查询PKMS,以判断这些

Package是否还存在。如果系统中已经不存在这些Package,则grants表需要更新

*/

purgeOldGrants();

/*

accounts.db中有一个accounts表,该表中存储了账户类型和账户名。其中,账户类型

就是AuthenticatorDescription中的accountType,它和具体应用有关。下面这个

函数将比较accounts表中的内容与AccountAuthenticatorCache中服务的信息,如果

AccountAuthenticatorCache已经不存在对应账户类型的服务,则需要删除accounts表

中的对应项

*/

validateAccountsAndPopulateCache();

}

AccountManagerService的构造函数较简单,有兴趣的读者可自行研究以上代码中未详细分析的函数。下面将通过一个具体的例子来分析AccountManagerService的工作流程。

8.3.2  AccountManageraddAccount分析

这一节将分析AccountManagerService中的一个重要的函数,即addAccount,其功能是为某项账户添加一个用户。下面以前面提及的Email为例来认识AAS的处理流程。

AccountManagerService是一个运行在SystemServer中的服务,客户端进程必须借助AccountManager提供的API来使用AccountManagerService服务,所以,本例需从AccountManager的addAccount函数讲起。

1.  AccountManager的addAccount发起请求

AccountManager 的addAccount函数的参数和返回值较复杂,先看其函数原型:

public AccountManagerFuture<Bundle>addAccount(

finalString accountType,

finalString authTokenType,

finalString[] requiredFeatures,

finalBundle addAccountOptions,

finalActivity activity,

AccountManagerCallback<Bundle>callback,

Handlerhandler)

在以上代码中:

·  addAccount的返回值类型是AccountManagerFuture<Bundle>。其中,AccountManagerFuture是一个模板Interface,其真实类型只有在分析addAccount的实现时才能知道。现在可以告诉读者的是,它和Java并发库(concurrent库)中的FutureTask有关,是对异步函数调用的一种封装[①]。调用者在后期只要调用它的getResult函数即可取得addAccount的调用结果。由于addAccount可能涉及网络操作(例如,AAS需要把账户添加到网络服务器上),所以这里采用了异步调用的方法以避免长时间的阻塞。这也是AccountManagerFuture的getResult不能在主线程中调用的原因。

·  addAccount的第一个参数accountType代表账户类型。该参数不能为空。就本例而言,它的值为“com.android.email”。

·  authTokenType、requiredFeatures和addAccountOptions与具体的AAS服务有关。如果想添加指定账户类型的Account,则须对其背后的AAS有所了解。

·  activity:此参数和界面有关。例如有些AAS需要用户输入用户名和密码,故需启动一Activity。在这种情况下,AAS会返回一个Intent,客户端将通过这个activity启动Intent所标示的Activity。读者将通过下文的分析了解这一点。

·  callback和handler:这两个参数与如何获取addAccount返回结果有关。如这两个参数为空,客户端则须单独启动一个线程去调用AccountManagerFuture的getResult函数。

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());

//构造一个匿名类对象,该类继承自AmsTask,并实现了doWork函数。addAccount返回前

//将调用该对象的start函数

returnnew AmsTask(activity, handler, callback) {

publicvoid doWork() throws RemoteException {

//mService用于和AccountManagerService通信

mService.addAcount(mResponse, accountType, authTokenType,

requiredFeatures, activity!= null, optionsIn);

}

}.start();

}

在以上代码中,AccountManager的 addAccount函数将返回一个匿名类对象,该匿名类继承自AmsTask类。那么,AmsTask又是什么呢?

(1) AmsTask介绍

先来看AmsTask的继承关系,如图8-7所示。

图8-7  AmsTask继承关系

由图8-7可知:

·  AmsTask继承自FutureTask,并实现了AccountManagerFuture接口。FutureTask是Java concurrent库中一个常用的类。AmsTask定义了一个doWork虚函数,该函数必须由子类来实现。

·  一个AmsTask对象中有一个mResponse成员,该成员的类型是AmsTask中的内部类Response。从Response的派生关系可知,Response将参与Binder通信,并且它是Binder通信的Bn端。而AccountManagerService的addAccount将得到它的Bp端对象。当添加完账户后,AccountManagerService会通过这个Bp端对象的onResult或onError函数向Response通知处理结果。

(2) AmsTask匿名类处理分析

AccountManager的addAccount最终返回给客户端的是一个AmsTask的子类,首先来了解它的构造函数,其代码如下:

[-->AccountManager.java::AmsTask]

public AmsTask(Activity activity, Handler handler,

AccountManagerCallback<Bundle> callback){

......//调用基类构造函数

//保存客户端传递的参数

mHandler= handler;

mCallback = callback;

mActivity = activity;

mResponse = new Response();//构造一个Response对象,并保存到mResponse中

}

下一步调用的是这个匿名类的start函数,代码如下:

[-->AccountManager.java::AmsTask.start]

public final AccountManagerFuture<Bundle>start() {

try {

doWork(); //调用匿名类实现的doWork函数

}......

returnthis;

}

匿名类实现的doWork函数即下面这个函数:

[-->AccountManager.java::addAccount返回的匿名类]

public void doWork() throws RemoteException {

//调用AccountManagerService的addAccount函数,其第一个参数是mResponse

mService.addAcount(mResponse, accountType, authTokenType,

requiredFeatures, activity != null, optionsIn);

}

AccountManager的addAccount函数的实现比较新奇,它内部使用了Java的concurrent类。不熟悉Java并发编程的读者有必要了解相关知识。

下面转到AccountManagerService中去分析addAccount的实现。

2. AccountManagerService addAccount转发请求

AccountManagerServiceaddAccount的代码如下所示:

[-->AccountManagerService.java::addAccount]

public void addAcount(finalIAccountManagerResponse response,

finalString accountType, final String authTokenType,

final String[] requiredFeatures, final boolean expectActivityLaunch,

final Bundle optionsIn) {

......

//检查客户端进程是否有“android.permission.MANAGE_ACCOUNTS”的权限

checkManageAccountsPermission();

final intpid = Binder.getCallingPid();

final intuid = Binder.getCallingUid();

//构造一个Bundle类型的options变量,并保存传入的optionsIn

finalBundle options = (optionsIn == null) ? new Bundle() : optionsIn;

options.putInt(AccountManager.KEY_CALLER_UID, uid);

options.putInt(AccountManager.KEY_CALLER_PID, pid);

longidentityToken = clearCallingIdentity();

try {

//创建一个匿名类对象,该匿名类派生自Session类。最后调用该匿名类的bind函数

new Session(response, accountType, expectActivityLaunch,true){

public void run() throws RemoteException {

mAuthenticator.addAccount(this, mAccountType,

authTokenType,requiredFeatures, options);

}

protected String toDebugString(longnow) {

......//实现toDebugString函数

}

}.bind();

}finally {

restoreCallingIdentity(identityToken);

}

}

由以上代码可知,AccountManagerService的addAccount函数最后也创建了一个匿名类对象,该匿名类派生自Session。addAccount最后还调用了这个对象的bind函数。其中最重要的内容就是Session。那么,Session又是什么呢?

(1) Session介绍

Session家族成员如图8-8所示。

图8-8  Session家族示意图

由图8-8可知:

·  Session从IAccountAuthenticatorResponse.Stub派生,这表明它将参与Binder通信,并且它是Bn端。那么这个Binder通信的目标是谁呢?,它正是具体的AAS服务。AccountManagerService会将自己传递给AAS,这样,AAS就得到IAccountAuthenticatorResponse的Bp端对象。当AAS完成了具体的账户添加工作后,会通过IAccountAuthenticatorResponse的Bp端对象向Seession返回处理结果。

·  Session通过mResponse成员变量指向来自客户端的IAccountManagerResponse接口,当Session收到AAS的返回结果后,又通过IAccountManagerResponse 的Bp端对象向客户端返回处理结果。

·  Session mAuthenticator变量的类型是IAccountAuthenticator,它用于和远端的AAS通信。客户端发起的请求将通过Session经由mAuthenticator调用对应AAS中的函数。

由图8-7和图8-8可知,AccountManagerService在addAccount流程中所起的是桥梁作用,具体如下:

·  客户端将请求发送给AccountManagerService,然后AccountManagerService再转发给对应的AAS。

·  AAS处理完的结果先返回给AccountManagerService,再由AccountManagerService返回给客户端。

由于图8-7和图8-8中定义的类名较相似,因此读者阅读时应仔细一些。

下面来看Session匿名类的处理。

(2) Session匿名类处理分析

首先调用Session的构造函数,代码为:

[-->AccountManagerService.java::Session]

public Session(IAccountManagerResponse response,String accountType,

boolean expectActivityLaunch, boolean stripAuthTokenFromResult) {

super();

......

/*

注意其中的参数,expectActivityLaunch由客户端传来,如果用户调用

AccountManager addAccount时传入了activity参数,则该值为true,

stripAuthTokenFromResult的默认值为true

*/

mStripAuthTokenFromResult = stripAuthTokenFromResult;

mResponse = response;

mAccountType = accountType;

mExpectActivityLaunch = expectActivityLaunch;

mCreationTime = SystemClock.elapsedRealtime();

synchronized(mSessions) {

//将这个匿名类对象保存到AccountManagerService中的mSessions成员中

mSessions.put(toString(), this);

}

try{  //监听客户端死亡消息

response.asBinder().linkToDeath(this, 0);

} ......

}

获得匿名类对象后,addAccount将调用其bind函数,该函数由AmsTask实现,代码如下:

[-->AccountManagerService.java::Session:bind]

void bind() {

//绑定到mAccountType指定的AAS。在本例中,AAS的类型是“com.android.email”

if(!bindToAuthenticator(mAccountType)) {

onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bindfailure");

}

}

bindToAuthenticator的代码为:

[-->AccountManagerService.java::Session:bindToAuthenticator]

private boolean bindToAuthenticator(StringauthenticatorType) {

//从mAuthenticatorCache中查询满足指定类型的服务信息

AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>

authenticatorInfo =

mAuthenticatorCache.getServiceInfo(

AuthenticatorDescription.newKey(authenticatorType));

......

Intentintent = new Intent();

intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);

//设置目标服务的ComponentName

intent.setComponent(authenticatorInfo.componentName);

//通过bindService启动指定的服务,成功与否将通过第二个参数传递的

//ServiceConnection接口返回

if(!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE)) {

......

}

returntrue;

}

由以上代码可知,Session的bind函数将启动指定类型的Service,这是通过bindService函数完成的。如果服务启动成功,Session的onServiceConnected函数将被调用,这部分代码如下:

[-->AccountManagerService.java::Session:onServiceConnected]

public void onServiceConnected(ComponentName name,IBinder service) {

//得到远端AAS返回的IAccountAuthenticator接口,这个接口用于

//AccountManagerService和该远端AAS交互

mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);

try {

run();//调用匿名类实现的run函数

} ......

}

匿名类实现的run函数非常简单,代码如下:

[-->AccountManagerService.java::addAccount返回的匿名类]

new Session(response, accountType,expectActivityLaunch,true) {

publicvoid run() throws RemoteException {

//调用远端AAS实现的addAccount函数

mAuthenticator.addAccount(this, mAccountType,

authTokenType, requiredFeatures,options);

}

由以上代码可知,AccountManagerService在addAccount最终将调用AAS实现的addAccount函数。

下面来看本例中满足“com.android.email”类型的服务是如何处理addAccount的请求的。该服务就是Email应用中的EasAuthenticatorService,下面来分析它。

3.  EasAuthenticatorService处理请求

EasAuthenticatorService的创建是AccountManagerService调用了bindService导致的,该函数会触发EasAuthenticatorService的onBind函数被调用,这部分代码如下:

[-->EasAuthenticatorService.java::onBind]

public IBinder onBind(Intent intent) {

if(AccountManager.ACTION_AUTHENTICATOR_INTENT.equals(

intent.getAction())){

//创建一个EasAuthenticator类型的对象,并调用其getIBinder函数

return new EasAuthenticator(this).getIBinder();

}else  return null;

}

下面来分析EasAuthenticator。

(1) EasAuthenticator介绍

EasAuthenticator是EasAuthenticatorService定义的内部类,其家族关系如图8-9所示。

图8-9  EasAuthenticator家族类图

由图8-9可知:

·  EasAuthenticator从AbstractAccountAuthenticator类派生。AbstractAccountAuthenticator内部有一个mTransport的成员变量,其类型是AbstractAccountAuthenticator的内部类Transport。在前面的onBind函数中,EasAuthenticator的getIBinder函数返回的就是这个变量。

·  Transport类继承自Binder,故它将参与Binder通信,并且是IAccountAuthenticator的Bn端。Session匿名类通过onServiceConnected函数将得到一个IAccountAuthenticator的Bp端对象。

当由AccoutManagerService的addAccount创建的那个Session匿名类调用IAccountAuthenticator Bp端对象的addAccount时,将触发位于Emai进程中的IAccountAuthenticatorBn端的addAccount。下面来分析Bn端的addAccount函数。

(2) EasAuthenticator的 addAccount函数分析

根据上文的描述可知,Emai 进程中首先被触发的是IAccountAuthenticatorBn端的addAccount函数,其代码如下:

[-->AbstractAccountAuthenticator.java::Transport:addAccount]

private class Transport extendsIAccountAuthenticator.Stub {

publicvoid addAccount(IAccountAuthenticatorResponse response,

String accountType, String authTokenType,

String[] features, Bundle options)

throws RemoteException {

//检查权限

checkBinderPermission();

try {

//调用AbstractAccountAuthenticator子类实现的addAccount函数

final Bundle result =

AbstractAccountAuthenticator.this.addAccount(

new AccountAuthenticatorResponse(response),

accountType,authTokenType, features, options);

//如果返回的result不为空,则调用response的onResult返回结果。

//这个response是IAccountAuthenticatorResponse类型,它的onResult

//将触发位于Session匿名类中mResponse变量的onResult函数被调用

if (result != null)

response.onResult(result);

}......

}

本例中AbstractAccountAuthenticator子类(即EasAuthenticator)实现的addAccount函数,代码如下:

[-->EasAuthenticatorService.java::EasAuthenticator.addAccount]

public BundleaddAccount(AccountAuthenticatorResponse response,

String accountType, StringauthTokenType,

String[] requiredFeatures, Bundle options)

throws NetworkErrorException {

//EasAuthenticatoraddAccount的处理逻辑和Email应用有关。只做简单了解即可

//如果用户传递的账户信息保护了密码和用户名,则走if分支。注意,其中有一些参数名是

//通用的,例如OPTIONS_PASSWORD,OPTIONS_USERNAME等

if(options != null && options.containsKey(OPTIONS_PASSWORD)

&& options.containsKey(OPTIONS_USERNAME)) {

//创建一个Account对象,该对象仅包括两个成员,一个是name,用于表示账户名;

//另一个是type,用于表示账户类型

finalAccount account = new

Account(options.getString(OPTIONS_USERNAME),

AccountManagerTypes.TYPE_EXCHANGE);

//调用AccountManager的addAccountExplicitly将account对象和password传递

//给AccountManagerService处理。读者可自行研究这个函数,在其内部将这些信息写入

//accounts.db的account表中

AccountManager.get(EasAuthenticatorService.this).

addAccountExplicitly(account,

options.getString(OPTIONS_PASSWORD), null);

//根据Email应用的规则,下面将判断和该账户相关的数据是否需要设置自动数据同步

//首先判断是否需要处理联系人自动数据同步

booleansyncContacts = false;

if(options.containsKey(OPTIONS_CONTACTS_SYNC_ENABLED) &&

options.getBoolean(OPTIONS_CONTACTS_SYNC_ENABLED))

syncContacts = true;

ContentResolver.setIsSyncable(account,

ContactsContract.AUTHORITY,1);

ContentResolver.setSyncAutomatically(account,

ContactsContract.AUTHORITY,syncContacts);

booleansyncCalendar = false;

......//判断是否需要设置Calendar自动数据同步

booleansyncEmail = false;

//如果选项中包含Email同步相关的功能,则需要设置Email数据同步的相关参数

if(options.containsKey(OPTIONS_EMAIL_SYNC_ENABLED) &&

options.getBoolean(OPTIONS_EMAIL_SYNC_ENABLED))

syncEmail = true;

/*

下面这两个函数将和ContentService中的SyncManager交互。注意这

两个函数:第一个函数setIsSyncable将设置Email对应的同步服务功能标志,

第二个函数setSyncAutomatically将设置是否自动同步Email。

数据同步的内容留待8.4节再详细分析

*/

ContentResolver.setIsSyncable(account,EmailContent.AUTHORITY, 1);

ContentResolver.setSyncAutomatically(account,EmailContent.AUTHORITY,

syncEmail);

//构造返回结果,注意,下面这些参数名是Android统一定义的,如KEY_ACCOUNT_NAME等

Bundleb = new Bundle();

b.putString(AccountManager.KEY_ACCOUNT_NAME,

options.getString(OPTIONS_USERNAME));

b.putString(AccountManager.KEY_ACCOUNT_TYPE,

AccountManagerTypes.TYPE_EXCHANGE);

returnb;

} else {

//如果没有传递password,则需要启动一个Activity,该Activity对应的Intent

//由actionSetupExchangeIntent返回。注意下面几个通用的参数,如

// KEY_ACCOUNT_AUTHENTICATOR_RESPONSE和KEY_INTENT

Bundle b = new Bundle();

Intent intent =AccountSetupBasics.actionSetupExchangeIntent(

EasAuthenticatorService.this);

intent.putExtra(

AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);

b.putParcelable(AccountManager.KEY_INTENT,intent);

return b;

}

}

不同的AAS有自己特定的处理逻辑,以上代码向读者展示了EasAuthenticatorService的工作流程。虽然每个AAS的处理方式各有不同,但Android还是定义了一些通用的参数,例如OPTIONS_USERNAME用于表示用户名,OPTIONS_PASSWORD用于表示密码等。关于这方面内容,读者可查阅SDK中AccountManager的文档说明。

4.  返回值的处理流程

在EasAuthenticator的addAccount返回处理结果后,AbstractAuthenticator将通过IAccountAuthenticatorResponse的onResult将其返回给由AccountManagerService创建的Session匿名类对象。来看Session的onResult函数,其代码如下:

[-->AccountManagerService.java::Session:onResult]

public void onResult(Bundle result) {

mNumResults++;

//从返回的result中取出相关信息,关于下面这个if分支处理和状态栏中相关的逻辑,

//读者可自行研究    if (result != null&&!TextUtils.isEmpty(

result.getString(AccountManager.KEY_AUTHTOKEN))) {

String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);

String accountType =

result.getString(AccountManager.KEY_ACCOUNT_TYPE);

if(!TextUtils.isEmpty(accountName) &&

!TextUtils.isEmpty(accountType)){

Account account = new Account(accountName, accountType);

cancelNotification(

getSigninRequiredNotificationId(account));

}

}

IAccountManagerResponse response;

//如果客户端传递了activity参数,则mExpectActivityLaunch为true。如果

//AAS返回的结果中包含KEY_INTENT,则表明需要弹出Activity以输入账户和密码

if(mExpectActivityLaunch && result != null

&& result.containsKey(AccountManager.KEY_INTENT)) {

response = mResponse;

} else {

/*

getResponseAndClose返回的也是mResponse,不过它会调用unBindService

断开和AAS服务的连接。就整个流程而言,此时addAccount已经完成AAS和

AccountManagerService的工作,故无需再保留和AAS服务的连接。而由于上面的if

分支还需要输入用户密码,因此以AAS和AccountManagerService之间的工作

还没有真正完成

*/

response = getResponseAndClose();

}

if(response != null) {

try{

......

if (mStripAuthTokenFromResult)

result.remove(AccountManager.KEY_AUTHTOKEN);

//调用位于客户端的IAccountManagerResponse的onResult函数

response.onResult(result);

} ......

}

}

客户端的IAccountManagerResponse接口由AmsTask内部类Response实现,其代码为:

[-->AccountManager.java::AmsTask::Response.onResult]

public void onResult(Bundle bundle) {

Intentintent = bundle.getParcelable(KEY_INTENT);

//如果需要弹出Activity,则要利用客户端传入的那个activity去启动AAS指定的

//Activity。这个Activity由AAS返回的Intent来表示

if(intent != null && mActivity != null) {

mActivity.startActivity(intent);

} elseif (bundle.getBoolean("retry")) {

//如果需要重试,则再次调用doWork

try {

doWork();

}......

}else{

//将返回结果保存起来,当客户端调用getResult时,就会得到相关结果

set(bundle);

}

}

5.  AccountManager的addAccount分析总结

AccountManager的addAccount流程分析起来给人一种行云流水般的感觉。该流程涉及3个模块,分别是客户端、AccountManagerService和AAS。整体难度虽不算大,但架构却比较巧妙。

总结起来addAccount相关流程如图8-10所示。

图8-10  AccountManager的addAccount处理流程

为了让读者看得更清楚,图8-10中略去了一些细枝末节的内容。另外,图8-10中第10步的onServiceConnected函数应由位于SystemServer中的ActivityThread对象调用,为方便阅读起见,这里没有画出ActivityThread的对象。

8.3.3  AccountManagerService的分析总结

本节对AccountManagerService进行分析,从技术上说,本节涉及和Java concurrent类相关的知识。另外,对并发编程来说,架构设计是最重要的,因此读者务必阅读脚注中提到的参考书籍《Pattern.Oriented.Software.Architecture.Volume.2》。

就整体而言,AccountManagerService及相关类的设计非常巧妙,读者不妨重温RegisteredServicesCache的结构及addAccount的处理流程并认真体会。

8.4  数据同步管理SyncManager分析

本节将分析ContentService中负责数据同步管理的SyncManager。SynManager和AccountManagerService之间的关系比较紧密。同时,由于数据同步涉及手机中重要数据(例如联系人信息、Email、日历等)的传输,因此它的控制逻辑非常严谨,知识点也比较多,难度相对比AccountManagerService大。

先来认识数据同步管理的核心类SyncManager。

8.4.1  初识SyncManager

SyncManager的构造函数的代码较长,可分段来看。下面先来介绍第一段的代码。

1.  SyncManager介绍

[-->SyncManager.java::SyncManager]

public SyncManager(Context context, booleanfactoryTest) {

mContext= context;

//SyncManager中的几位重要成员登场。见下文的解释

SyncStorageEngine.init(context);

mSyncStorageEngine =SyncStorageEngine.getSingleton();

mSyncAdapters = newSyncAdaptersCache(mContext);

mSyncQueue = newSyncQueue(mSyncStorageEngine, mSyncAdapters);

HandlerThread syncThread = newHandlerThread("SyncHandlerThread",

Process.THREAD_PRIORITY_BACKGROUND);

syncThread.start();

mSyncHandler = new SyncHandler(syncThread.getLooper());

mMainHandler = new Handler(mContext.getMainLooper());

/*

mSyncAdapters类似AccountManagerService中的AccountAuthenticatorCache,

它用于管理系统中和SyncService相关的服务信息。下边的函数为mSyncAdapters增加一个

监听对象,一旦系统中的SyncService发生变化(例如安装了一个提供同步服务的APK包),则

SyncManager需要针对该服务发起一次同步请求。同步请求由scheduleSync函数发送,

后文再分析此函数

*/

mSyncAdapters.setListener(new

RegisteredServicesCacheListener<SyncAdapterType>() {

public void onServiceChanged(SyncAdapterType type,

boolean removed) {

if (!removed) {

scheduleSync(null, type.authority, null, 0,false);

}

}

},mSyncHandler);

在以上代码中,首先见到的是SyncManager的几位重要成员,它们之间的关系如图8-11所示。

图8-11  SyncManager成员类图

由图8-11可知, SyncManager的这几位成员的功能大体可分为3部分:

·  左上角是SyncAdaptersCache类,从功能和派生关系上看,它和AccountManagerService中的AccountAuthenticatorCaches类似。SyncAdaptersCache用于管理系统中SyncService服务的信息。在SyncManager中,SyncService的信息用SyncAdapterType类来表示。

·  左下角是SyncQueue和SyncOperation类,SyncOperation代表一次正在执行或等待执行的同步操作,而SyncQueue通过mOperationsMap保存系统中存在的SyncOperation。

·  右半部分是SyncStorageEngine,由于同步操作涉及,重要数据的传输,加之可能耗时较长,所以SyncStorageEngine提供了一些内部类来保存同步操作中的一些信息。例如PendingOperation代表保存在从本地文件中的那些还没有执行完的同步操作的信息。另外,SyncStrorageEngine还需要对同步操作进行一些统计,例如耗电量统计等。SyncStorageEngine包含的内容较多,读者学习完本章后可自行研究相关内容。

SyncManager家族成员的责任分工比较细,后续分析时再具体讨论它们的作用。先接着看SyncManager的构造函数:

[-->SyncManager.java::SyncManager]

......

//创建一个用于广播发送的PendingIntent,该PendingIntent用于和

//AlarmManagerService交互

mSyncAlarmIntent= PendingIntent.getBroadcast(

mContext, 0, new Intent(ACTION_SYNC_ALARM),0);

//注册CONNECTIVITY_ACTION广播监听对象,用于同步操作需要使用网络,所以

//此处需要监听和网络相关的广播

IntentFilter intentFilter =

new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);

context.registerReceiver(mConnectivityIntentReceiver, intentFilter);

if(!factoryTest) {

//监听BOOT_COMPLETED广播

intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);

context.registerReceiver(mBootCompletedReceiver, intentFilter);

}

//监听BACKGROUND_DATA_SETTING_CHANGED广播。该广播与是否允许后台传输数据有关,

//用户可在Settings应用程序中设置对应选项

intentFilter =

new IntentFilter(ConnectivityManager.

ACTION_BACKGROUND_DATA_SETTING_CHANGED);

context.registerReceiver(mBackgroundDataSettingChanged, intentFilter);

//监视设备存储空间状态广播。由于SyncStorageEngine会保存同步时的一些信息到存储

//设备中,所以此处需要监视存储设备的状态

intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);

intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);

context.registerReceiver(mStorageIntentReceiver, intentFilter);

//监听SHUTDOWN广播。此处设置优先级为100,即优先接收此广播

intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);

intentFilter.setPriority(100);

context.registerReceiver(mShutdownIntentReceiver, intentFilter);

if(!factoryTest) {//和通知服务交互,用于在状态栏上提示用户

mNotificationMgr = (NotificationManager)

context.getSystemService(Context.NOTIFICATION_SERVICE);

//注意,以下函数注册的广播将针对前面创建的mSyncAlarmIntent

context.registerReceiver(new SyncAlarmIntentReceiver(),

newIntentFilter(ACTION_SYNC_ALARM));

}......

mPowerManager = (PowerManager)

context.getSystemService(Context.POWER_SERVICE);

//创建WakeLock,防止同步过程中掉电

mHandleAlarmWakeLock =

mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,

HANDLE_SYNC_ALARM_WAKE_LOCK);

mHandleAlarmWakeLock.setReferenceCounted(false);

mSyncManagerWakeLock =

mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,

SYNC_LOOP_WAKE_LOCK);

mSyncManagerWakeLock.setReferenceCounted(false);

//知识点一:监听SyncStorageEngine的状态变化,如下文解释

mSyncStorageEngine.addStatusChangeListener(

ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,

newISyncStatusObserver.Stub() {

public void onStatusChanged(int which) {

sendCheckAlarmsMessage();

}

});

//知识点二:监视账户的变化。如果用户添加或删除了某个账户,则需要做相应处理。

//如下文解释

if(!factoryTest) {

AccountManager.get(mContext).addOnAccountsUpdatedListener(

SyncManager.this,

mSyncHandler, false);

onAccountsUpdated(AccountManager.get(mContext).getAccounts());

}

}

在以上代码中,有两个重要知识点。

第一, SyncManager为SyncStorageEngine设置了一个状态监听对象。根据前文的描述,在SyncManager家族中,SyncStorageEngine专门负责管理和保存同步服务中绝大部分的信息,所以当外界修改了这些信息时,SyncStorageEngine需要通知状态监听对象。我们可以通过一个例子了解其中的工作流程。下面的setSyncAutomatically函数的作用是设置是否自动同步某个账户的某项数据,代码如下:

[-->ContentService.java::setSyncAutomatically]

public void setSyncAutomatically(Account account,String providerName,

booleansync) {

......//检查WRITE_SYNC_SETTINGS权限

longidentityToken = clearCallingIdentity();

try {

SyncManager syncManager = getSyncManager();

if(syncManager != null) {

/*

通过SyncManager找到SyncStorageEngine,并调用它的

setSyncAutomatically函数。在其内部会修改对应账户的同步服务信息,然后通知

监听者,而这个监听者就是SyncManager设置的那个状态监听对象

*/

syncManager.getSyncStorageEngine().setSyncAutomatically(

account, providerName,sync);

}

}finally {

restoreCallingIdentity(identityToken);

}

}

在以上代码中,最终调用的是SyncStorageEngine的函数,但SyncManager也会因状态监听对象被触发而做出相应动作。实际上,ContentService中大部分设置同步服务参数的API,其内部实现就是先直接调用SyncStorageEngine的函数,然后再由SyncStorageEngine通知监听对象。读者在阅读代码时,仔细一些就可明白这一关系。

第二,SyncManager将为AccountManager设置一个账户更新监听对象(注意,此处是AccountManager,而不是AccountManagerService。AccountManager这部分功能的代码不是很简单,读者有必要反复研究)。在Android平台上,数据同步和账户的关系非常紧密,并且同一个账户可以对应不同的数据项。例如在EasAuthenticator的addAccount实现中,读者会发现一个Exchange账户可以对应Contacts、Calendar和Email三种不同的数据项。在添加Exchange账户时,还可以选择是否同步其中的某项数据(通过判断实现addAccount时传递的options是否含有对应的同步选项,例如同步邮件数据时需要设置的OPTIONS_EMAIL_SYNC_ENABLED选项)。由于SyncManager和账户之间的这种紧密关系的存在,SyncManager就必须监听手机中账户的变化情况。

提示上述两个知识点涉及的内容都是一些非常细节的问题,本章拟将它们作为小任务,读者可自行研究它们。

下面来认识一下SyncManager家族中的几位主要成员,首先是SyncStorageEngine。

2.  SyncStorageEngine介绍

SyncStorageEngine负责整个同步系统中信息管理方面的工作。先看其init函数代码:

[-->SyncStorageEngine.java::init]

public static void init(Context context) {

if(sSyncStorageEngine != null) return;

/*

得到系统中加密文件系统的路径,如果手机中没有加密的文件系统(根据系统属性

“persist.security.efs.enabled”的值来判断),则返回的路径为/data,

目前的Android手机大部分都没有加密文件系统,故dataDir为/data

*/

FiledataDir = Environment.getSecureDataDirectory();

//创建SyncStorageEngine对象

sSyncStorageEngine = new SyncStorageEngine(context, dataDir);

}

而SyncStorageEngine的构造函数代码为:

[-->SyncStorageEngine.java::SyncStorageEngine]

private SyncStorageEngine(Context context, FiledataDir) {

mContext= context;

sSyncStorageEngine = this;

mCal =Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));

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

FilesyncDir = new File(systemDir, "sync");

syncDir.mkdirs();

//mAccountInfoFile指向/data/system/sync/accounts.xml

mAccountInfoFile = new AtomicFile(new File(syncDir,"accounts.xml"));

//mStatusFile指向/data/system/sync/status.bin,该文件记录

//一些和同步服务相关的状态信息

mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));

//mStatusFile指向/data/system/sync/pending.bin,该文件记录了当前处于pending

//状态的同步请求

mPendingFile = new AtomicFile(new File(syncDir,"pending.bin"));

//mStatusFile指向/data/system/sync/stats.bin,该文件记录同步服务管理运行过程

//中的一些统计信息

mStatisticsFile = new AtomicFile(new File(syncDir,"stats.bin"));

/*

解析上述四个文件,从下面的代码看,似乎是先读(readXXX)后写(writeXXX),这是怎么

回事?答案在于AtomicFile,它内部实际包含两个文件,其中一个用于备份,防止数据丢失。

感兴趣的读者可以研究AtomicFile类,其实现非常简单

*/

readAccountInfoLocked();

readStatusLocked();

readPendingOperationsLocked();

readStatisticsLocked();

readAndDeleteLegacyAccountInfoLocked();

writeAccountInfoLocked();

writeStatusLocked();

writePendingOperationsLocked();

writeStatisticsLocked();

}

上述init和SyncStorage的构造函数都比较简单,故不再详述。下面将讨论一些有关accounts.xml的故事。以下是一个真实机器上的accounts.xml文件,如图8-12所示。

图8-12  accounts.xml内容展示

图8-12所示为笔者KindleFire(CM9的ROM)机器中accounts.xml文件的内容,其中两个黑框中内容的作用如下:

·  第一个框中的listen-for-tickles,该标签和Android平台中的Master Sync有关。Master Sync用于控制手机中是否所有账户对应的所有数据项都自动同步。用户可通过ContentResolver setMasterSyncAutomatically进行设置。

·  第二个框代表一个AuthorityInfo。AuthorityInfo记录了账户和SyncService相关的一些信息。此框中的account为笔者的邮箱,type为“com.google”。另外,从图8-12中还可发现,一个账户(包含account和type两个属性)可以对应多种类型的数据项,例如此框中对应的数据项是“com.android.email.provider”,而此框前面一个AuthorityInfo对应的数据项是“com.google.android.apps.books”。AuthorityInfo中的periodicSync用于控制周期同步的时间,单位是秒,默认是86400秒,也就是1天。另外,AuthorityInfo中还有一个重要属性syncable,它的可选值为true、false或unknown(在代码中,这3个值分别对应整型值1、0和-1)。

syncable的unknown状态是个较难理解的概念,它和参数SYNC_EXTRAS_INITIALIZE有关,官方的解释如下:

/**

Set by theSyncManager to request that the SyncAdapter initialize itself for

the givenaccount/authority pair. One required initialization step is to

ensure thatsetIsSyncable()has been called with a >= 0 value.

When thisflag is set the SyncAdapter does not need to do a full sync,

though itis allowed to do so.

*/

publicstatic final String SYNC_EXTRAS_INITIALIZE = "initialize";

由以上解释可知,如果某个SyncService的状态为unknown,那么在启动它时必须传递一个SYNC_EXTRAS_INITIALIZE选项,SyncService解析该选项后即可知自己尚未被初始化。当它完成初始化后,需要调用setIsSyncable函数设置syncable的状态为1。另外,SyncService初始化完成后,是否可接着执行同步请求呢?目前的设计是,它们并不会立即执行同步,需要用户再次发起请求。读者在后续小节中会看到与此相关的处理。

此处先来看setIsSyncable的使用示例,前面分析的EasAuthenticator addAccount中有如下的函数调用:

//添加完账户后,将设置对应的同步服务状态为1

ContentResolver.setIsSyncable(account,EmailContent.AUTHORITY, 1);

ContentResolver.setSyncAutomatically(account,EmailContent.AUTHORITY,

syncEmail);

在EasAuthenticator中,一旦添加了账户,就会设置对应SyncService的syncable状态为1。SyncManager将根据这个状态做一些处理,例如立即发起一次同步操作。

注意是否设置syncable状态和具体应用有关,图8-12第二个框中的gmail邮件同步服务就没有因为笔者添加了账户而设置syncable为1。

3.  SyncAdaptersCache介绍

再看SyncAdaptersCache,其构造函数代码如下:

[-->SyncAdaptersCache.java::SyncAdaptersCache]

SyncAdaptersCache(Context context) {

/*

调用基类RegisteredServicesCache的构造函数,其中SERVICE_INTERFACE和

和SERVICE_META_DATA的值为“android.content.SyncAdapter”,

ATTRIBUTES_NAME为字符串"sync-adapter"

*/

super(context, SERVICE_INTERFACE, SERVICE_META_DATA,

ATTRIBUTES_NAME, sSerializer);

}

SyncAdaptersCache的基类是RegisteredServicesCache。8.3.1节已经分析过RegisteredServicesCache了,此处不再赘述。下面展示一个实际的例子,如图8-13所示。

图8-13  android.content.SyncAdapter.xml

图8-13列出了笔者KindleFire上安装的同步服务,其中黑框列出的是“om.android.exchange”,该项服务和Android中Exchange应用有关。Exchange的AndroidManifest.xml文件的信息如图8-14所示。

图8-14  Exchange AndroidManifest.xml示意

图8-14列出了Exchange应用所支持的针对邮件提供的同步服务,即EmailSyncAdapterService。该服务会通过meta-data中的resource来描述自己,这部分内容如图8-15所示。

图8-15  syncadapter_email.xml内容展示

图8-15告诉我们,EmailSyncAdapterService对应的账户类型是“com.android.exchange”,需要同步的邮件数据地址由contentAuthority表示,即本例中的“com.android.email.provider”。注意,EmailSyncAdapterService只支持从网络服务端同步数据到本机,故supportsUploading为false。

再看SyncManager家族中最后一位成员SyncQueue。

4.  SyncQueue介绍

SyncQueue用于管理同步操作对象SyncOperation。SyncQueue的构造函数代码为:

[-->SyncQueue.java::SyncQueue]

public SyncQueue(SyncStorageEnginesyncStorageEngine,

finalSyncAdaptersCache syncAdapters) {

mSyncStorageEngine = syncStorageEngine;

//从SyncStorageEngine中取出上次没有完成的同步操作信息,这类信息由

//PendingOperations表示

ArrayList<SyncStorageEngine.PendingOperation> ops

= mSyncStorageEngine.getPendingOperations();

final intN = ops.size();

for (inti=0; i<N; i++) {

SyncStorageEngine.PendingOperation op = ops.get(i);

//从SyncStorageEngine中取出该同步操作的backoff信息

finalPair<Long, Long> backoff =

syncStorageEngine.getBackoff(op.account,op.authority);

//从SyncAdaptersCache中取出该同步操作对应的同步服务信息,如果同步服务已经不存在,

//则无须执行后面的流程

finalRegisteredServicesCache.ServiceInfo<SyncAdapterType>

syncAdapterInfo= syncAdapters.getServiceInfo(

SyncAdapterType.newKey(op.authority,

op.account.type));

if (syncAdapterInfo == null)  continue;

//构造一个SyncOperation对象

SyncOperation syncOperation = newSyncOperation(

op.account, op.syncSource, op.authority, op.extras, 0,

backoff != null ? backoff.first : 0,

syncStorageEngine.getDelayUntilTime(op.account, op.authority),

syncAdapterInfo.type.allowParallelSyncs());

syncOperation.expedited = op.expedited;

syncOperation.pendingOperation = op;

//将SyncOperation对象保存到mOperationsMap变量中

add(syncOperation, op);

}

}

SyncQueue比较简单,其中一个比较难理解的概念是backoff,后文再对此作解释。

至此,SyncManager及相关家族成员已介绍完毕。下面将通过实例分析同步服务的工作流程。在本例中,将同步Email数据,目标同步服务为EmailSyncAdapterService。

8.4.2 ContentResolver 的requestSync分析

ContentResolver提供了一个requestSync函数,用于发起一次数据同步请求。在本例中,该函数的调用方法如下:

Account emailSyncAccount = newAccount("[email protected]",

"com.google");

String emailAuthority ="com.android.email.provider";

Bundle emailBundle = new Bundle();

......//为emailBundle添加相关的参数。这些内容和具体的同步服务有关

//发起Email同步请求

ContentResolver.requesetSync(emailSyncAccount,emailAuthority,emailBundle);

1.  客户端发起请求

ContentResolver requestSync的代码如下:

[-->ContentResolver.java::requestSync]

public static void requestSync(Account account,String authority,

Bundle extras) {

//检查extras携带的参数的数据类型,目前只支持float、int和String等几种类型

validateSyncExtrasBundle(extras);

try {

//调用ContentService的requestSync函数

getContentService().requestSync(account, authority, extras);

}......

}

与添加账户(addAccount)相比,客户端发起一次同步请求所要做的工作就太简单了。

下面转战ContentService去看它的requestSync函数。

2.  ContentService 的requestSync函数分析

[-->ContentService.java::requestSync]

public void requestSync(Account account, Stringauthority, Bundle extras) {

ContentResolver.validateSyncExtrasBundle(extras);

longidentityToken = clearCallingIdentity();

try {

SyncManager syncManager = getSyncManager();

if(syncManager != null) {

//调用syncManager的scheduleSync

syncManager.scheduleSync(account, authority, extras,

0,false);

}

}finally {

restoreCallingIdentity(identityToken);

}

}

ContentService将工作转交给SyncManager来完成,其调用的函数是scheduleSync。

(1) SyncManager的scheduleSync函数分析

先行介绍的scheduleSync函数非常重要。

/*

scheduleSync一共5个参数,其作用分别如下。

requestedAccount表明要进行同步操作的账户。如果为空,SyncManager将同步所有账户。

requestedAuthority表明要同步的数据项。如果为空,SyncManager将同步所有数据项。

extras指定同步操作中的一些参数信息。这部分内容后续分析时再来介绍。

delay指定本次同步请求是否延迟执行。单位为毫秒。

onlyThoseWithUnkownSyncableState是否只同步那些处于unknown状态的同步服务。该参数

在代码中没有注释。结合前面对syncable为unknown的分析,如果该参数为true,则

本次同步请求的主要作用就是通知同步服务进行初始化操作

*/

public void scheduleSync(Account requestedAccount,String requestedAuthority,

Bundleextras, long delay,boolean onlyThoseWithUnkownSyncableState)

关于scheduleSync的代码将分段分析,其相关代码如下:

[-->SyncManager.java::scheduleSync]

boopublic void scheduleSync(AccountrequestedAccount,

StringrequestedAuthority, Bundle extras,

long delay, boolean onlyThoseWithUnkownSyncableState)

//判断是否允许后台数据传输

finalboolean backgroundDataUsageAllowed = !mBootCompleted ||

getConnectivityManager().getBackgroundDataSetting();

if (extras== null) extras = new Bundle();

//下面将解析同步服务中特有的一些参数信息,下面将逐条解释

//SYNC_EXTRAS_EXPEDITED参数表示是否立即执行。如果设置了该选项,则delay参数不起作用

//delay参数用于设置延迟执行时间,单位为毫秒

Booleanexpedited = extras.getBoolean(

ContentResolver.SYNC_EXTRAS_EXPEDITED,false);

if (expedited)

delay = -1;

Account[]accounts;

if (requestedAccount != null) {

accounts = new Account[]{requestedAccount};

} ......

//SYNC_EXTRAS_UPLOAD参数设置本次同步是否对应为上传。从本地同步到服务端为Upload,

//反之为download

finalboolean uploadOnly = extras.getBoolean(

ContentResolver.SYNC_EXTRAS_UPLOAD, false);

//SYNC_EXTRAS_MANUAL等同于SYNC_EXTRAS_IGNORE_BACKOFF加

//SYNC_EXTRAS_IGNORE_SETTINGS

final boolean manualSync = extras.getBoolean(

ContentResolver.SYNC_EXTRAS_MANUAL, false);
   //如果是手动同步,则忽略backoff和settings参数的影响

if(manualSync) {

//知识点一:SYNC_EXTRAS_IGNORE_BACKOFF:该参数和backoff有关,见下文的解释

extras.putBoolean(

ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);

//SYNC_EXTRAS_IGNORE_SETTINGS:忽略设置

extras.putBoolean(

ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);

}

finalboolean ignoreSettings = extras.getBoolean(

ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS,false);

//定义本次同步操作的触发源,见下文解释

int source;

if(uploadOnly) {

source = SyncStorageEngine.SOURCE_LOCAL;

} else if(manualSync) {

source = SyncStorageEngine.SOURCE_USER;

} else if(requestedAuthority == null) {

source = SyncStorageEngine.SOURCE_POLL;

} else {

source = SyncStorageEngine.SOURCE_SERVER;

}

在以上代码中,有两个知识点需要说明。

知识点一和backoff有关:这个词不太好翻译。和其相关的应用场景是,如果本次同步操作执行失败,则尝试休息一会再执行,而backoff在这个场景中的作用就是控制休息时间。由以上代码可知,当用户设置了手动(Manual)参数后,就无须对这次同步操作使用backoff模式。

另外,在后续的代码中,我们会发现和backoff有关的数据被定义成一个Paire<Long,Long>,即backoff对应两个参数。这两个参数到底有什么用呢?笔者在SyncManager代码中找到了一个函数,其参数的命名很容易理解。该函数是setBackoff,原型如下:

[-->SyncManager.java::setBackoff]

public void setBackoff(Account account, StringproviderName,

long nextSyncTime, long nextDelay)

在调用这个函数时,Pair<Long,Long>中的两个参数分别对应nextSyncTime和nextDelay,所以,Pair中的第一个参数对应nextSyncTime,第二个参数对应nextDelay。backoff的计算中实际上存在着一种算法。它是什么呢?读者不妨先研究setBackoff,然后再和我们一起分享。

知识点二和SyncStorageEngine定义的触发源有关。说白了,触发源就是描述该次同步操作是因何而起的。SyncStorageEngine一共定义了4种类型的源,这里笔者直接展示其原文解释:

/* Enumvalue for a local-initiated sync. */

public static final int SOURCE_LOCAL = 1;

/**  Enum value for a poll-based sync (e.g., upon connectionto network)*/

public static final int SOURCE_POLL = 2;

/* Enumvalue for a user-initiated sync. */

public static final int SOURCE_USER = 3;

/* Enumvalue for a periodic sync. */

publicstatic final int SOURCE_PERIODIC = 4;

触发源的作用主要是为了SyncStorageEngine的统计工作。本节不打算深究这部分内容,感兴趣的读者可在学习完本节后自行研究。

关于scheduleSync下一阶段的工作,代码如下:

[-->SyncManager.java::scheduleSync]

//从SyncAdaptersCache中取出所有SyncService信息

final HashSet<String>syncableAuthorities = new HashSet<String>();

for(RegisteredServicesCache.ServiceInfo<SyncAdapterType>

syncAdapter :mSyncAdapters.getAllServices()) {

syncableAuthorities.add(syncAdapter.type.authority);

}

//如果指定了本次同步的authority,则从上述同步服务信息中找到满足要求的SyncService

if(requestedAuthority != null) {

final boolean hasSyncAdapter =

syncableAuthorities.contains(requestedAuthority);

syncableAuthorities.clear();

if(hasSyncAdapter) syncableAuthorities.add(requestedAuthority);

}

finalboolean masterSyncAutomatically =

mSyncStorageEngine.getMasterSyncAutomatically();

for(String authority : syncableAuthorities) {

for(Account account : accounts) {

//取出AuthorityInfo中的syncable状态,如果为1,则syncable为true,

//如果为-1,则状态为unknown

intisSyncable = mSyncStorageEngine.getIsSyncable(

account,authority);

if(isSyncable == 0) continue;//syncable为false,则不能进行同步操作

final RegisteredServicesCache.ServiceInfo<SyncAdapterType>

syncAdapterInfo =

mSyncAdapters.getServiceInfo(

SyncAdapterType.newKey(authority, account.type));

......

//有些同步服务支持多路并发同步操作

final boolean allowParallelSyncs =

syncAdapterInfo.type.allowParallelSyncs();

finalboolean isAlwaysSyncable = syncAdapterInfo.type.

isAlwaysSyncable();

//如果该同步服务此时的状态为unknown,而它又是永远可同步的(AlwaysSyncable),

//那么通过setIsSyncable设置该服务的状态为1

if(isSyncable < 0 && isAlwaysSyncable) {

mSyncStorageEngine.setIsSyncable(account, authority, 1);

isSyncable = 1;

}

//如果只操作unknow状态的同步服务,并且该服务的状态不是unknown,则不允许后续操作

if(onlyThoseWithUnkownSyncableState && isSyncable >= 0)

continue;

//如果此同步服务不支持上传,而本次同步又需要上传,则不允许后续操作

if(!syncAdapterInfo.type.supportsUploading() && uploadOnly)

continue;

//判断是否允许执行本次同步操作。如果同步服务状态为unknown,则总是允许发起同步请求,

//因为这时的同步请求只是为了初始化SyncService

boolean syncAllowed = (isSyncable < 0) ||ignoreSettings

|| (backgroundDataUsageAllowed && masterSyncAutomatically

&& mSyncStorageEngine.getSyncAutomatically(

account,authority));

......

//取出对应的backoff参数

Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(

account,authority);

//获取延迟执行时间

longdelayUntil = mSyncStorageEngine.getDelayUntilTime(

account,authority);

finallong backoffTime = backoff != null ? backoff.first : 0;

if(isSyncable < 0) {

Bundle newExtras = new Bundle();

//如果syncable状态为unknown,则需要设置一个特殊的参数,即

//SYNC_EXTRAS_INITIALIZE,它将通知SyncService进行初始化操作

newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);

scheduleSyncOperation(

new SyncOperation(account, source, authority, newExtras, 0,

backoffTime,delayUntil,allowParallelSyncs));

}

if(!onlyThoseWithUnkownSyncableState)

scheduleSyncOperation(

new SyncOperation(account,source, authority, extras, delay,

backoffTime, delayUntil,allowParallelSyncs));

}//for循环结束

}

}

scheduleSync函数较复杂,难点在于其策略控制。建议读者反复阅读这部分内容。

scheduleSync最后将构造一个SyncOperation对象,并调用scheduleSyncOperation处理它。scheduleSyncOperation内部会将这个SyncOperation对象保存到mSyncQueue中,然后发送MESSAGE_CHECK_ALARMS消息让mSyncHandler去处理。由于scheduleSyncOperation函数比较简单,因此下面将直接去mSyncHandler的handleMessage函数中分析MESSAGE_CHECK_ALARMS的处理过程。

(2) 处理MESSAGE_CHECK_ALARMS消息

SyncHandler的handleMessage代码如下:

[-->SyncManager.java::SyncHandler:handleMessage]

public void handleMessage(Message msg) {

longearliestFuturePollTime = Long.MAX_VALUE;

longnextPendingSyncTime = Long.MAX_VALUE;

try {

waitUntilReadyToRun();

mDataConnectionIsConnected = readDataConnectionState();

//获得WakeLock,防止在同步过程中掉电

mSyncManagerWakeLock.acquire();

//处理周期同步的操作

earliestFuturePollTime= scheduleReadyPeriodicSyncs();

switch (msg.what) {

......

case SyncHandler.MESSAGE_CHECK_ALARMS:

//调用maybeStartNextSyncLocked函数,返回一个时间。见下文解释

nextPendingSyncTime = maybeStartNextSyncLocked();

break;

......

}//switch结束

} finally{

manageSyncNotificationLocked();

/*

将上边函数调用的返回值传递给manageSyncAlarmLocked,该函数内部与

AlarmManagerService交互,其实就是定义一个定时提醒。在Alarm超时后,就会广播

在SyncManager构造函数中定义的那个PendingIntent mSyncAlarmIntent,

而SyncManager收到该广播后又会做对应处理。相关内容读者可自行阅读

*/

manageSyncAlarmLocked(earliestFuturePollTime,nextPendingSyncTime);

mSyncTimeTracker.update();

mSyncManagerWakeLock.release();

}

}

如以上代码所述,MESSAGE_CHECK_ALARMS消息的处理就是调用maybeStartNextSyncLocked函数。这个函数内容较繁琐,它主要做了以下几项工作。

·  检查SyncQueue中保存的同步操作对象SyncOperation,判断它们对应的同步服务的状态是否为false,如果为false,则不允许执行该同步操作。

·  查询ConnectivityManagerService以判断目标同步服务是否使用了网络。如果该服务当前没有使用网络,则不允许执行该同步操作。

·  判断同步操作对象的执行时间是否已到,如果未到,则不允许执行该操作。

·  将通过上述判断的同步操作对象SyncOperation与当前系统中正在执行的同步操作上下文对象进行比较。系统当前正在执行的同步操作上下文对象对应的数据类是ActiveSyncContext,它是在同步操作对象之上的一个封装,包含了能和同步服务交互的接口。由于并非所有同步服务都支持多路并发同步操作,因此这里需做一些处理,以避免不必要的同步操作。另外,如一个仅对应初始化同步服务的同步操作执行时间过长(由系统属性“sync.max_time_per_sync”控制,默认是5分钟),系统也需做一些处理。

提示maybeStartNextSyncLocked是笔者在本节留给读者自行分析的函数中最难的一个。读者务必阅读完下面的分析后,尝试去研究此函数。

通过上述层层考验后,manageSyncAlarmLocked最后将调用dispatchSyncOperation真正去派发一个同步操作。下面来看dispatchSyncOperation的代码。

[-->SyncManager.java::dispatchSyncOperation]

private booleandispatchSyncOperation(SyncOperation op) {

SyncAdapterType syncAdapterType = SyncAdapterType.

newKey(op.authority,op.account.type);

RegisteredServicesCache.ServiceInfo<SyncAdapterType>

syncAdapterInfo=

mSyncAdapters.getServiceInfo(syncAdapterType);

......

//构造一个ActiveSyncContext对象,它就是前面提到的同步操作上下文对象

ActiveSyncContextactiveSyncContext =

new ActiveSyncContext(op,

insertStartSyncEvent(op), syncAdapterInfo.uid);

activeSyncContext.mSyncInfo =

mSyncStorageEngine.addActiveSync(activeSyncContext);

// mActiveSyncContexts保存了当前系统中所有的ActiveSyncContext对象

mActiveSyncContexts.add(activeSyncContext);

//为该对象绑定到具体的同步服务上

if(!activeSyncContext.bindToSyncAdapter(syncAdapterInfo)) {

closeActiveSyncContext(activeSyncContext);

return false;

}

returntrue;

}

ActiveSyncContext是SyncManager和同步服务交互的关键类,其家族图谱如图8-16所示。

图8-16  ActiveSyncContext的 UML类图

图8-16中的ActiveSyncContext和图8-8中的Session非常像。ActiveSyncContext的主要工作包括下面两部分。

·  它将首先通过bindService方式启动SyncService,并在onServiceConnected函数中得到用于和SyncService交互的接口对象,即参与Binder通信的ISyncAdapterBp端。

·  ActiveSyncContext是ISyncContext接口的Binder通信的Bn端,它在调用ISyncAdapter的startSync时,会把自己传递给同步服务。同步服务得到的当然是ISyncContext的Bp端对象。当同步服务完成此次同步操作后就会调用ISyncContext 的Bp端对象的onFinished函数以通知ActiveSyncContext同步操作的执行结果。

下面再看第一部分的工作。

(3) ActiveSyncContext派发请求

[-->SyncManager.java::ActiveSyncContext.bindToSyncAdapter]

booleanbindToSyncAdapter(RegisteredServicesCache.ServiceInfo info) {

Intentintent = new Intent();

intent.setAction("android.content.SyncAdapter");

//设置目标同步服务的ComponentName

intent.setComponent(info.componentName);

intent.putExtra(Intent.EXTRA_CLIENT_LABEL,

com.android.internal.R.string.sync_binding_label);

intent.putExtra(Intent.EXTRA_CLIENT_INTENT,

PendingIntent.getActivity(

mContext, 0,

new Intent(Settings.ACTION_SYNC_SETTINGS),0));

mBound= true;

//调用bindService启动指定的同步服务

finalboolean bindResult = mContext.bindService(intent, this,

Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND

| Context.BIND_ALLOW_OOM_MANAGEMENT);

if(!bindResult)

mBound = false;

returnbindResult;

}

当目标SyncService从其onBind函数返回后,ActiveSyncContext的onServiceConnected将被调用,该函数的内部处理流程如下:

[-->SyncManager.java::ActiveSyncContext.onServiceConnected]

public void onServiceConnected(ComponentName name,IBinder service) {

Messagemsg = mSyncHandler.obtainMessage();

msg.what= SyncHandler.MESSAGE_SERVICE_CONNECTED;

//构造一个ServiceConnectionData对象,并发送MESSAGE_SERVICE_CONNECTED消息

//给mSyncHandler。第二个参数就是SyncService在onBind函数中返回的ISyncAdapter的

//Binder通信对象。不过在ActiveSyncContext中,它是Bp端

msg.obj= new ServiceConnectionData(this,

ISyncAdapter.Stub.asInterface(service));

mSyncHandler.sendMessage(msg);

}

[-->SyncManager.java::SyncHandler.handleMessage]

case SyncHandler.MESSAGE_SERVICE_CONNECTED: {

ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;

if(isSyncStillActive(msgData.activeSyncContext))

//调用runBoundToSyncAdapter函数处理

runBoundToSyncAdapter(msgData.activeSyncContext,

msgData.syncAdapter);

break;

}

[-->SyncManager.java::runBoundToSyncAdapter]

private void runBoundToSyncAdapter(final ActiveSyncContextactiveSyncContext,

ISyncAdapter syncAdapter) {

activeSyncContext.mSyncAdapter = syncAdapter;

finalSyncOperation syncOperation = activeSyncContext.mSyncOperation;

try {

activeSyncContext.mIsLinkedToDeath = true;

syncAdapter.asBinder().linkToDeath(activeSyncContext, 0);

//调用目标同步服务的startSync函数

syncAdapter.startSync(activeSyncContext, syncOperation.authority,

syncOperation.account, syncOperation.extras);

} ......

}

对SynManager工作的分析到此为止,下面将分析目标同步服务。

3. EmailSyncAdapterService处理请求

在本例中,目标同步服务位于EmailSyncAdapterService中,先看它通过onBind函数返回给ActiveSyncContext的是什么。

(1) onBind分析

[-->EmailSyncAdapterService.java::onBind]

public IBinder onBind(Intent intent) {

//sSyncAdapter是EmailSyncAdapterService的内部类对象,见下文解释

returnsSyncAdapter.getSyncAdapterBinder();

}

在以上代码中,sSyncAdapter的类型是EmailSyncAdapterService中的内部类SyncAdapterImpl。它的派生关系如图8-17所示。

图8-17  SyncAdapterImpl派生关系图

有图8-17可知:

·  AbstractThreadSyncAdapter是核心类,其内部有一个成员变量mISyncAdapterIml,该变量用于和ActiveSyncContext交互,是ISyncAdapter Binder通信的Bn端。该对象也是以上代码中onBind函数的返回值。

·  SyncThread从Thread派生。从这一点可看出,同步服务将创建工作线程来执行具体的同步操作。AbstractThreadSyncAdapter中的mSyncThreads保存该同步服务中所有的SyncThread对象。

·  同步操作的结果将通过SyncResult返给SyncManager。

再看SyncManager runBoundToSyncAdapter函数最后调用的startSync函数。

(2) startSync分析

在SyncService中,首先被调用的函数是ISyncAdapterImpl的startSync函数,其代码为:

[-->AbstractThreadedSyncAdapter.java::ISyncAdapterImpl.startSync]

public void startSync(ISyncContext syncContext,String authority,

Account account,Bundle extras) {

//构造一个SyncContext对象,用于保存上下文信息

finalSyncContext syncContextClient = new SyncContext(syncContext);

booleanalreadyInProgress;

finalAccount threadsKey = toSyncKey(account);

synchronized (mSyncThreadLock) {

//判断是否存在已经在执行的SyncThread

if(!mSyncThreads.containsKey(threadsKey)) {

if (mAutoInitialize

&& extras != null&& extras.getBoolean(

ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {

//一般而言,mAutoInitialize都为true,表示同步服务支持自动初始化

//如果该服务对应的syncable状态为unknown,则重新设置syncable为1

if (ContentResolver.getIsSyncable(account, authority) < 0)

 ContentResolver.setIsSyncable(account,authority, 1);

//直接返回,不再做后续的处理,实际上后续的流程是可以继续进行的

syncContextClient.onFinished(new SyncResult());

return;

}

//创建一个新的SyncThread对象

SyncThread syncThread = new SyncThread(

"SyncAdapterThread-" +

mNumSyncStarts.incrementAndGet(),

syncContextClient,authority, account, extras);

mSyncThreads.put(threadsKey, syncThread);

syncThread.start();//启动工作线程

alreadyInProgress = false;

}else {

alreadyInProgress = true;

}

}

if(alreadyInProgress)

syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);

}

假如尚未匹配的工作线程(根据account生成一个key作为标示来查找是否已经存在对应的工作线程),SyncService将创建一个SyncThread,其run函数代码是:

[-->AbstractThreadedSyncAdapter.java::ISyncAdapterImpl.run]

public void run() {

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

SyncResult syncResult = new SyncResult();

ContentProviderClient provider = null;

try {

if(isCanceled()) return;

//获得同步操作指定的ContentProvider,provider是ContentProviderClient

//类型,用于和目标ContentProvider交互

provider = mContext.getContentResolver().

acquireContentProviderClient(mAuthority);

if (provider != null) {

//调用AbstractThreadedSyncAdapter子类的onPerformSync函数

AbstractThreadedSyncAdapter.this.onPerformSync(mAccount,

mExtras,mAuthority,provider, syncResult);

} else

syncResult.databaseError = true;

}finally {

if (provider != null)

provider.release();

if (!isCanceled()) //通知结果

mSyncContext.onFinished(syncResult);

//工作完成,将该线程从mSyncThreads中移除

synchronized (mSyncThreadLock) {

mSyncThreads.remove(mThreadsKey);

}

}

}

来看AbstractThreadedSyncAdapter子类实现的onPeroformSync函数,在本例中,子类是SyncAdapterImpl,代码如下:

[-->EmailSyncAdapterService.java::SyncAdapterImpl.onPerformSync]

public void onPerformSync(Account account, Bundleextras, String authority,

ContentProviderClient provider,SyncResult syncResult) {

try {

//调用EmailSyncAdapterService performSync完成真正的同步,这部分代码和

//Email业务逻辑相关,此处不再深入研究

EmailSyncAdapterService.performSync(mContext, account, extras,

authority, provider,syncResult);

}......

}

执行完onPerformSync函数后,ISyncAdapterImpl.run返回前会调用mSyncContext.onFinished函数,向位于SyncManager中的ActiveSyncContext通知同步操作的结果。读者可自行研究这部分内容。

4.  ContentResolver requestSync分析总结

总结requestSync的工作流程,如图8-18所示。

图8-18  requestSync流程

由图8-18可知,requestSync涉及的对象及调用流程比较繁琐。但从技术上看,则没有什么需要特别注意的地方。

8.4.3  数据同步管理SyncManager分析总结

本节对ContentService中第二个主要功能即数据同步管理SyncManager进行了研究,这部分内容主要包括3个方面:

·  介绍SyncManager及其相关成员的作用。

·  通过requestSync展示了SyncManager各个模块的作用及交互过程。

·  穿插于上述两方面之中的是数据同步的一些处理策略和规则。笔者未对这部分内容展开更细致的讨论。感兴趣的读者可自行学习。

8.5  本章学习指导

本章对ContentService和AccountManagerService进行了研究,在学习过程中,需注意以下几方面。

·  ContentService包括两个不同部分,一个是数据更新通知机制,另一个是同步服务管理。在这两部分中,ContentService承担了数据更新通知机制的工作,同步服务管理的工作则委托给SyncManager来完成。基于这种分工,本章先分析了ContentService的数据更新通知机制。这部分内容非常简单,读者应能轻松掌握。不过,作为深入学习的入口,笔者有意留了几个问题,旨在让读者仔细思考。

·  由于同步服务管理和AccountManagerService关系密切,因此本章先分析了AccountManagerService。在这部分代码中,读者需要了解RegisteredServicesCache的作用。另外,感兴趣的读者也可以学习如何编写AuthenticatorService,相关文档在SDK关于AbstractAccountAuthenticator类的说明中。

·  SyncManager是本章理解难度最大的知识点。笔者觉得,其难度主要体现在同步策略上。另外,缺乏相关文档资料的参考也是导致学习SyncManager难度较大的原因之一。读者在把本节内容搞清楚的基础上,可自行研究本章没有提及的内容。另外,读者如想学习如何编写同步服务,可参考SDK文档中关于AbstractThreadSyncAdapter的说明。

除上述内容外,AccountManager的ddAccount函数实现中所使用的Java concurrent类,也是读者必须学习并掌握的基础知识。

8.6  本章小结

本章对ContentService和AccountManagerService进行了较为深入的探讨,其中:

·  8.2和8.4节分别分析了ContentService中数据更新通知机制的实现及同步服务管理方面的工作。

·  8.3节分析了AccountManagerService及addAccount的实现。

本章留出以下问题供读者自行研究,这些问题包括:

·  8.2节最后提到的开放性问题和Cursor query中ContentObserver的使用分析。

·  8.4节涉及SyncManager的同步请求处理的策略和maybeStartNextSyncLocked函数分析等。另外,读者如果有兴趣,不妨研究一下SyncStorageEngine中同步信息的统计等内容。


[①] 从设计模式角度来说,这属于ActiveObject模式。详细内容可阅读《Pattern.Oriented.Software.Architecture.Volume.2》的第2章“Concurrency Patterns”。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-25 15:59:05

[深入理解Android卷二 全文-第八章]深入理解ContentService和AccountManagerService的相关文章

[深入理解Android卷一全文-第八章]深入理解Surface系统

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版.而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. 第8章  深入理解Surface系统 本章主要内容 ·  具体分析一个Activity的显示过程. ·  具体分析Surface. ·  具体分析SurfaceFlinger. 本章涉及的源代码文件名称及位置: · ActivityThread.java framework/base/core/java/an

[深入理解Android卷二 全文-第四章]深入理解PackageManagerService

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容 第4章  深入理解PackageManagerService 本章主要内容: 详细分析PackageManagerService 本章所涉及的源代码文件名及位置: ·  SystemServer.java frameworks/base/services/java/com/android/server/Syst

[深入理解Android卷二 全文-第七章]深入理解ContentProvider

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容 第7章  深入理解ContentProvider 本章主要内容: ·  深入分析ContentProvider的创建和启动,以及SQLite相关的知识点 ·  深入分析Cursor query和close函数的实现 ·  深入分析ContentResolver openAssetFileDescriptor函数

[深入理解Android卷二 全文-第五章]深入理解PowerManagerService

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容 第5章  深入理解PowerManagerService 本章主要内容: ·  深入分析PowerManagerService ·  深入分析BatteryService和BatteryStatsService 本章所涉及的源代码文件名及位置: ·  PowerManagerService.java frame

[深入理解Android卷二 全文-第六章]深入理解ActivityManagerService

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容 第6章 深入理解ActivityManagerService 本章主要内容: ·  详细分析ActivityManagerService 本章所涉及的源代码文件名及位置: ·  SystemServer.java frameworks/base/services/java/com/android/server/

[深入理解Android卷二 全文-第三章]深入理解SystemServer

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在osc博客中全文转发这两本书的全部内容 第3章  深入理解SystemServer 本章主要内容: ·  分析SystemServer ·  分析EntropyService.DropBoxManagerService.DiskStatsService ·  分析DeviceStorageMonitorService.SamplingProfilerS

[深入理解Android卷一 全文-第二章]深入理解JNI

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. (出版社排版好的PDF版正在向出版社申请,到时候会通过CSDN下载资源发布) 第2章  深入理解JNI 本章主要内容 ·  通过一个实例,介绍JNI技术和在使用中应注意的问题. 本章涉及的源代码文件名及位置 下面是本章分析的源码文件名及其位置. ·  MediaScanner.java framework/b

[深入理解Android卷一全文-第十章]深入理解MediaScanner

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在OSC博客中全文转发这两本书的全部内容. 第10章 深入理解MediaScanner 本章主要内容 ·  介绍多媒体系统中媒体文件扫描的工作原理. 本章涉及的源代码文件名及位置 下面是本章分析的源码文件名及其位置. ·  MediaProvider.java packages/providers/MediaProvider/MediaProvider.

[深入理解Android卷一全文-第九章]深入理解Vold和Rild

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. 第9章  深入理解Vold和Rild 本章主要内容 ·  介绍Vold. ·  介绍Rild. 本章涉及的源代码文件名称及位置 下面是本章分析的源码文件名及其位置. ·  Main.cpp system/vold/Main.cpp ·  NetlinkManager.cpp system/vold/Netli