SyncManager | Android数据同步管理

864 阅读6分钟

1.1 概述

SyncManager是Android提供的一个同步框架,该框架实施了许多最佳做法,它允许Android应用使用Google应用中实现高效同步的一个基本框架。该功能是 ContentService 中负责数据同步管理的 SyncManager ,ContentService 承担了数据更新通知机制的工作,同步服务管理的工作则委托给 SyncManager 来完成。另外,可以通过SyncAdapter来使用该框架处理同步请求。

SyncAdapter适用于需要同步本地数据和在线账户信息的应用,如电子邮件的定时收取、笔记应用的云备份、天气应用的及时同步等。SyncAdapter设计为必须与用户账户绑定,即使你的应用不需要账户认证,也需要实现相关的类来处理账户,并可以将其隐藏。

我发现数据同步管理比较复杂,且参考资料不多,这里我对其进行简单的梳理。主要是从遇到的问题来进行分析的。

【问题描述】:

下拉快捷中有同步按钮,默认是关闭状态,之后使用 wifi 万能钥匙,发现该开关自动打开了。

1.2 调用流程

这里是根据问题进行代码跟踪的,我的关注点在下拉状态栏的同步按钮为什么会自动打开。研究发现,这里的开关主要是调用setMasterSyncAutomatically() 方法。

ContentResolver.java
    setMasterSyncAutomatically()
        ContentService.java
            setMasterSyncAutomatically()
                SyncManager.java
                    setMasterSyncAutomatically()
                        SyncStorageEngine.java
                            setMasterSyncAutomatically()
                                    // mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);

// frameworks/base/services/core/java/com/android/server/content/ContentService.java

 public void requestSync(Account account, String authority, Bundle extras,
596              String callingPackage) {
597          Bundle.setDefusable(extras, true);
598          ContentResolver.validateSyncExtrasBundle(extras);
599          int userId = UserHandle.getCallingUserId();
600          final int callingUid = Binder.getCallingUid();
601          final int callingPid = Binder.getCallingPid();
602  
603          validateExtras(callingUid, extras);
604          final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(callingUid, extras);
605  
606          // This makes it so that future permission checks will be in the context of this
607          // process rather than the caller's process. We will restore this before returning.
608          final long identityToken = clearCallingIdentity();
609          try {
610              SyncManager syncManager = getSyncManager();
611              if (syncManager != null) {
                     // 该处调用到  syncManager
612                  syncManager.scheduleSync(account, userId, callingUid, authority, extras,
613                          SyncStorageEngine.AuthorityInfo.UNDEFINED,
614                          syncExemption, callingUid, callingPid, callingPackage);
615              }
616          } finally {
617              restoreCallingIdentity(identityToken);
618          }
619      }

当同步模式改变时,系统会向外界发送广播 com.android.sync.SYNC_CONN_STATUS_CHANGED

setting 点击快捷时,可以调用 ContentResolver.setMasterSyncAutomaticallyAsUser() 方法来设置同步状态。

packages/apps/Settings/src/com/android/settings/users/AutoSyncDataPreferenceController.java

  public void onClick(DialogInterface dialog, int which) {
155              if (which == DialogInterface.BUTTON_POSITIVE) {
156                  ContentResolver.setMasterSyncAutomaticallyAsUser(mEnabling,
157                          mUserHandle.getIdentifier());
158                  if (mPreference != null) {
159                      mPreference.setChecked(mEnabling);
160                  }
161              }
162          }
163      }

1.2.1 同步判断条件

Android 同步框架发起同步判断条件,所有同步发起时会判断以下属性:

  1. Provider 的 isSyncable

  2. SyncAdapter 的 isAlwaysSyncable

  3. 自动同步(SYNC_EXTRAS_MANUAL 为 false) 发起时除以上之外,还要判断:

    • 系统总同步开关(getMasterSyncAutomatically 获取系统总同步开关状态 )
    • SyncAdapter 同步开关( getSyncAutomatically )

如果是通过调用 ContentResolver 的 notifyChange 发起自动同步时会带 SYNC_EXTRAS_UPLOAD 标志。

Android设计原意是仅将本地数据更新至服务端。此时SyncAdapter中的supportsUploading若是false,则不能发起自动同步。

1.2.2 getMasterSyncAutomatically()

我们这里看看 getMasterSyncAutomatically 这个方法,主要是获取系统级的总同步开关

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

3453      /**
3454       * Gets the global auto-sync setting that applies to all the providers and accounts.
3455       * If this is false then the per-provider auto-sync setting is ignored.
3456       * <p>This method requires the caller to hold the permission
3457       * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
3458       *
3459       * @return the global auto-sync setting that applies to all the providers and accounts
3460       */
          
3461      public static boolean getMasterSyncAutomatically() {
3462          try {
3463              return getContentService().getMasterSyncAutomatically();
3464          } catch (RemoteException e) {
3465              throw e.rethrowFromSystemServer();
3466          }
3467      }
frameworks/base/services/core/java/com/android/server/content/ContentService.java

          // 进一步调用到了这里
1040      public boolean getMasterSyncAutomatically() {
1041          return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
1042      }
1043  
1044      /**
1045       * If the user id supplied is different to the calling user, the caller must hold the
1046       * INTERACT_ACROSS_USERS_FULL permission.
1047       */
1048      @Override
1049      public boolean getMasterSyncAutomaticallyAsUser(int userId) {
1050          enforceCrossUserPermission(userId,
1051                  "no permission to read the sync settings for user " + userId);
1052          mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
1053                  "no permission to read the sync settings");
1054  
1055          final long identityToken = clearCallingIdentity();
1056          try {
1057              SyncManager syncManager = getSyncManager();
1058              if (syncManager != null) {
                      // 真正的实现是在 syncStorageEngine 中 
1059                  return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
1060              }
1061          } finally {
1062              restoreCallingIdentity(identityToken);
1063          }
1064          return false;
1065      }
1066  
    
    
    //frameworks/base/services/core/java/com/android/server/content/SyncStorageEngine.java
      public boolean getMasterSyncAutomatically(int userId) {
            // 这里 mAuthotities 为权限,同步锁
997          synchronized (mAuthorities) {
998              Boolean auto = mMasterSyncAutomatically.get(userId);
999              return auto == null ? mDefaultMasterSyncAutomatically : auto;
1000          }
1001      }

     // private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>();  布尔型的数组

1.2.3 setMasterSyncAutomatically()

这个方法是核心方法,主要用来设置同步开关,层层调用最后在 SyncStorageEngine 中实现。

// 该处同步状态发生改变的时候会调用,设置同步开关

973      public void setMasterSyncAutomatically(boolean flag, int userId,
974              @SyncExemption int syncExemptionFlag, int callingUid, int callingPid) {
975          mLogger.log("Set master enabled=", flag, " user=", userId,
976                  " cuid=", callingUid,
977                  " cpid=", callingPid);
978          synchronized (mAuthorities) {
979              Boolean auto = mMasterSyncAutomatically.get(userId);
                 // 状态不变时退出,不做改变
980              if (auto != null && auto.equals(flag)) {
981                  return;
982              }
                 // 往该数组中写入变更后的状态
983              mMasterSyncAutomatically.put(userId, flag);
984              writeAccountInfoLocked();
985          }
986          if (flag) {
                  // 做进一步的同步操作
987              requestSync(null, userId, SyncOperation.REASON_MASTER_SYNC_AUTO, null,
988                      new Bundle(),
989                      syncExemptionFlag, callingUid, callingPid);
990          }
991          reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
             // 发送同步状态变化的广播
992          mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
993          queueBackup();
994      }
// 通过堆栈打印,这里是进行 Binder 通信

06-28 10:49:17.995  2413  3536 D SyncManager: java.lang.Throwable
06-28 10:49:17.995  2413  3536 D SyncManager:   at com.android.server.content.SyncStorageEngine.setMasterSyncAutomatically(SyncStorageEngine.java:975)
06-28 10:49:17.995  2413  3536 D SyncManager:   at com.android.server.content.ContentService.setMasterSyncAutomaticallyAsUser(ContentService.java:1087)
06-28 10:49:17.995  2413  3536 D SyncManager:   at android.content.IContentService$Stub.onTransact(IContentService.java:866)
06-28 10:49:17.995  2413  3536 D SyncManager:   at android.os.Binder.execTransactInternal(Binder.java:1179)
06-28 10:49:17.995  2413  3536 D SyncManager:   at android.os.Binder.execTransact(Binder.java:1143)

这里应用调用 setMasterSyncAutomatically() 方法的话,是可以改变状态的。如果传过来的 flag 为 true 那么,同步开关就会打开。

ContentResolver.setMasterSyncAutomatically(true)

另外我们看看 mAuthorities, 它是所有可同步权限的列表,也是我们的同步锁。

434      // Primary list of all syncable authorities.  Also our global lock.
435      @VisibleForTesting
436      final SparseArray<AuthorityInfo> mAuthorities =
437              new SparseArray<AuthorityInfo>();

1.3 问题分析

【问题描述】: 用户在使用wifi万能钥匙等app应用时,之前默认关闭的同步开关,这时自动打开。

【分析过程】:

  1. 我在分析中发现,第一次启动 wifi 万能钥匙,这时同步开关会自动打开。
  2. 如果不杀 wifi 万能钥匙的后台,手动关闭同步开关,再次使用 WiFi 万能钥匙,则不会再自动打开。
  3. 杀掉 wifi 万能钥匙后再打开,这时同步开关会自动打开。

打开wifi万能钥匙的堆栈打印:

我们看mAuthorities 这里的打印,可以达到 wifi 万能钥匙的 mAuthorities 中的条件是满足的,且申请同步,所以这里设置为 true的,于是同步开关就打开了。但每个应用的表现不一样,应该是 wifi 万能钥匙中有调用ContentResolver.setMasterSyncAutomatically(true),第一次打开时想要进行数据同步。

06-28 11:39:44.778  2632  3597 D SyncManager: java.lang.Throwable
06-28 11:39:44.778  2632  3597 D SyncManager:   at com.android.server.content.SyncStorageEngine.setMasterSyncAutomatically(SyncStorageEngine.java:975)
06-28 11:39:44.778  2632  3597 D SyncManager:   at com.android.server.content.ContentService.setMasterSyncAutomaticallyAsUser(ContentService.java:1087)
06-28 11:39:44.778  2632  3597 D SyncManager:   at android.content.IContentService$Stub.onTransact(IContentService.java:866)
06-28 11:39:44.778  2632  3597 D SyncManager:   at android.os.Binder.execTransactInternal(Binder.java:1179)
06-28 11:39:44.778  2632  3597 D SyncManager:   at android.os.Binder.execTransact(Binder.java:1143)
06-28 11:39:44.778  2632  3597 D SyncManager: Set master enabled=true user=0 cuid=10355 cpid=16799
    
 // 这里我们可以达到 wifi 万能钥匙的 mAuthorities 中的条件是满足的,且申请同步,所以这里设置为 true的,于是同步开关就打开了
06-28 11:39:44.778  2632  3597 D SyncManager: mAuthorities{1=用户8133096253497/com.ss.android.ugc.aweme.AccountSyncProvider:u0, enabled=false, syncable=0, backoff=-1, delay=0, 2=sync.wifi.com/com.lantern.daemon.data.sync:u0, enabled=true,
 syncable=1, backoff=444635, delay=0, 3=account.wifi.com/com.lantern.daemon.data.sync.one:u0, enabled=true, syncable=1, backoff=445016, delay=0, 4=wifi.com/com.snda.wifilocating.sync.provider:u0, enabled=true, syncable=1, backoff=-1, delay=0, 5=accounts.wifi.com/com.lantern.daemon.data.sync.new:u0, enabled=true, syncable=-1, backoff=-1, delay=0}


06-28 11:39:44.783  2632  3597 D SyncManager: java.lang.Throwable
06-28 11:39:44.783  2632  3597 D SyncManager:   at com.android.server.content.SyncStorageEngine.getMasterSyncAutomatically(SyncStorageEngine.java:999)
06-28 11:39:44.783  2632  3597 D SyncManager:   at com.android.server.content.SyncManager.scheduleSync(SyncManager.java:1048)
06-28 11:39:44.783  2632  3597 D SyncManager:   at com.android.server.content.SyncManager.scheduleSync(SyncManager.java:886)
06-28 11:39:44.783  2632  3597 D SyncManager:   at com.android.server.content.SyncManager$7.onSyncRequest(SyncManager.java:578)
06-28 11:39:44.783  2632  3597 D SyncManager:   at com.android.server.content.SyncStorageEngine.requestSync(SyncStorageEngine.java:2391)
06-28 11:39:44.783  2632  3597 D SyncManager:   at com.android.server.content.SyncStorageEngine.setMasterSyncAutomatically(SyncStorageEngine.java:989)
06-28 11:39:44.783  2632  3597 D SyncManager:   at com.android.server.content.ContentService.setMasterSyncAutomaticallyAsUser(ContentService.java:1087)
06-28 11:39:44.783  2632  3597 D SyncManager:   at android.content.IContentService$Stub.onTransact(IContentService.java:866)
06-28 11:39:44.783  2632  3597 D SyncManager:   at android.os.Binder.execTransactInternal(Binder.java:1179)
06-28 11:39:44.783  2632  3597 D SyncManager:   at android.os.Binder.execTransact(Binder.java:1143)
06-28 11:39:44.783  2632  3597 D SyncManager: mMasterSyncAutomatically{0=true}autotrue

关于 ContentService 、SyncManager 跟详细的分析请看下一篇(未完待续)......

参考文档: blog.csdn.net/liuno0/arti…