多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
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所示。 :-: ![](http://img.blog.csdn.net/20150803131622061?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图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所示。 :-: ![](http://img.blog.csdn.net/20150803131638857?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图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所示。 :-: ![](http://img.blog.csdn.net/20150803131658964?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图8-13 android.content.SyncAdapter.xml 图8-13列出了笔者KindleFire上安装的同步服务,其中黑框列出的是“om.android.exchange”,该项服务和Android中Exchange应用有关。Exchange的AndroidManifest.xml文件的信息如图8-14所示。 :-: ![](http://img.blog.csdn.net/20150803131712854?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图8-14 Exchange AndroidManifest.xml示意 图8-14列出了Exchange应用所支持的针对邮件提供的同步服务,即EmailSyncAdapterService。该服务会通过meta-data中的resource来描述自己,这部分内容如图8-15所示。 :-: ![](http://img.blog.csdn.net/20150803131725373?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图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。