8.4.2 ContentResolver的requestSync分析

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


Account emailSyncAccount=new Account("fanping.deng@gmail",

"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, String authority, Bundle extras){

ContentResolver.validateSyncExtrasBundle(extras);

long identityToken=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,

Bundle extras, long delay, boolean onlyThoseWithUnkownSyncableState)


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

[—>SyncManager. java:scheduleSync]


boopublic void scheduleSync(Account requestedAccount,

String requestedAuthority, Bundle extras,

long delay, boolean onlyThoseWithUnkownSyncableState)

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

final boolean backgroundDataUsageAllowed=!mBootCompleted||

getConnectivityManager().getBackgroundDataSetting();

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

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

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

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

Boolean expedited=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

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

}

final boolean 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函数,其参数的命名很容易理解。setBackoff函数的原型如下:

[—>SyncManager. java:setBackoff]


public void setBackoff(Account account, String providerName,

long nextSyncTime, long nextDelay)


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

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


/Enum value for a local-initiated sync./

public static final int SOURCE_LOCAL=1;

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

public static final int SOURCE_POLL=2;

/Enum value for a user-initiated sync./

public static final int SOURCE_USER=3;

/Enum value for a periodic sync./

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

}

final boolean masterSyncAutomatically=

mSyncStorageEngine.getMasterSyncAutomatically();

for(String authority:syncableAuthorities){

for(Account account:accounts){

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

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

int isSyncable=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();

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

//获取延迟执行时间

long delayUntil=mSyncStorageEngine.getDelayUntilTime(

account, authority);

final long 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){

long earliestFuturePollTime=Long.MAX_VALUE;

long nextPendingSyncTime=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消息的处理就是调用maybeStartNext-SyncLocked函数。这个函数内容较烦琐,它主要做了以下几项工作。

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

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

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

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

提示 maybeStartNextSyncLocked是笔者在本节留给读者自行分析的函数中最难的一个。读

者务必阅读完下面的分析后,尝试去研究此函数。

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

[—>SyncManager. java:dispatchSyncOperation]


private boolean dispatchSyncOperation(SyncOperation op){

SyncAdapterType syncAdapterType=SyncAdapterType.

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

RegisteredServicesCache. ServiceInfo<SyncAdapterType>

syncAdapterInfo=

mSyncAdapters. getServiceInfo(syncAdapterType);

……

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

ActiveSyncContext activeSyncContext=

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;

}

return true;

}


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

8.4.2 ContentResolver的requestSync分析 - 图1

图 8-16 ActiveSyncContext的UML类图

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

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

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

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

(3)ActiveSyncContext派发请求

这部分的代码如下:

[—>SyncManager. java:ActiveSyncContext.bindToSyncAdapter]


boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info){

Intent intent=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启动指定的同步服务

final boolean bindResult=mContext.bindService(intent, this,

Context.BIND_AUTO_CREATE|Context.BIND_NOT_FOREGROUND

|Context.BIND_ALLOW_OOM_MANAGEMENT);

if(!bindResult)

mBound=false;

return bindResult;

}


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

[—>SyncManager. java:ActiveSyncContext.onServiceConnected]


public void onServiceConnected(ComponentName name, IBinder service){

Message msg=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 ActiveSyncContext activeSyncContext,

ISyncAdapter syncAdapter){

activeSyncContext.mSyncAdapter=syncAdapter;

final SyncOperation 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的内部类对象,见下文解释

return sSyncAdapter.getSyncAdapterBinder();

}


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

由图8-17可知:

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

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

8.4.2 ContentResolver的requestSync分析 - 图2

图 8-17 SyncAdapterImpl派生关系图

同步操作的结果将通过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对象,用于保存上下文信息

final SyncContext syncContextClient=new SyncContext(syncContext);

boolean alreadyInProgress;

final Account 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子类实现的onPerformSync函数,在本例中,子类是SyncAdapterImpl,代码如下:

[—>EmailSyncAdapterService. java:SyncAdapterImpl.onPerformSync]


public void onPerformSync(Account account, Bundle extras, 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.4.2 ContentResolver的requestSync分析 - 图3

图 8-18 requestSync流

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