ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
ContentResolver提供了一个requestSync函数,用于发起一次数据同步请求。在本例中,该函数的调用方法如下: ~~~ Account emailSyncAccount = newAccount("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, 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所示。 :-: ![](http://img.blog.csdn.net/20150803131747220?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图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所示。 :-: ![](http://img.blog.csdn.net/20150803131801930?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图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所示。 :-: ![](http://img.blog.csdn.net/20150803131817211?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图8-18 requestSync流程 由图8-18可知,requestSync涉及的对象及调用流程比较繁琐。但从技术上看,则没有什么需要特别注意的地方。