ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

第 32 章:账户与同步框架

第 32 章:账户与同步框架 Android 的账户与同步框架提供两个紧耦合的子系统:AccountManager用于凭证存储与身份令牌管理;SyncManager用于调度和执行后台数据同步。二者共同构成所有与远端服务器同步数据的应用的底层支撑,涵盖邮件、联系人、企业 MDM 以及第三方云服务。本章完整梳理整套架构:从面向应用的AccountManager与ContentResolver.requestSync()API,到 system_server 内部实现(AccountManagerService、SyncManager),再到底层数据库存储以及与JobScheduler的集成。32.1 AccountManager 架构32.1.1 三层设计账户子系统遵循 AOSP 标准分层架构:框架 API(Java)—android.accounts.AccountManager,面向应用的入口类,提供账户发现、凭证管理、身份令牌获取能力。系统服务—AccountManagerService运行于 system_server 进程,实现IAccountManagerAIDL 接口。管理账户数据库、认证器绑定、令牌缓存。认证器插件— 第三方或设备厂商实现AbstractAccountAuthenticator,负责对应账户类型实际的凭证校验与令牌生成。源码路径(关键文件):AccountManager ................. frameworks/base/core/java/android/accounts/AccountManager.java Account ........................ frameworks/base/core/java/android/accounts/Account.java AbstractAccountAuthenticator ... frameworks/base/core/java/android/accounts/AbstractAccountAuthenticator.java IAccountManager.aidl ........... frameworks/base/core/java/android/accounts/IAccountManager.aidl IAccountAuthenticator.aidl ..... frameworks/base/core/java/android/accounts/IAccountAuthenticator.aidl AccountManagerService .......... frameworks/base/services/core/java/com/android/server/accounts/AccountManagerService.java AccountsDb ..................... frameworks/base/services/core/java/com/android/server/accounts/AccountsDb.java TokenCache ..................... frameworks/base/services/core/java/com/android/server/accounts/TokenCache.java AccountAuthenticatorCache ...... frameworks/base/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java32.1.2 架构图32.1.3 AccountManager — 应用入口通过Context.getSystemService(Context.ACCOUNT_SERVICE)获取AccountManager;注解为@SystemService(Context.ACCOUNT_SERVICE)。源码:frameworks/base/core/java/android/accounts/AccountManager.java该类同时提供同步、异步接口。绝大多数方法返回AccountManagerFutureBundle,可搭配回调,也可阻塞调用getResult()。方法用途返回值getAccounts()获取设备上全部账户Account[]getAccountsByType(type)获取指定类型账户Account[]getAccountsByTypeAndFeatures(...)按类型、特性过滤账户FutureaddAccount(type, ...)拉起 UI 添加新账户Future(携带 KEY_INTENT 的 Bundle)removeAccount(account, ...)删除账户FuturegetAuthToken(account, type, ...)获取身份令牌Future(携带 KEY_AUTHTOKEN 的 Bundle)invalidateAuthToken(type, token)作废缓存令牌voidsetAuthToken(account, type, token)手动设置令牌voidgetPassword(account)读取账户密码(调用方受限)StringsetPassword(account, password)设置账户密码voidaddAccountExplicitly(account, password, extras)无 UI 方式添加账户booleansetUserData(account, key, value)存储账户键值对数据voidgetUserData(account, key)读取账户键值对数据StringaddOnAccountsUpdatedListener(...)注册账户变更监听void32.1.4 Account 数据模型Account是简单值类型,包含两个字段:// 取自 frameworks/base/core/java/android/accounts/Account.java public class Account implements Parcelable { public final String name; // 例如 "user@gmail.com" public final String type; // 例如 "com.google" }type是关键标识,将账户与对应认证器绑定。该值必须与认证器 XML 元数据内android:accountType属性保持一致。关联类型:类用途Account账户名称 + 类型组合AccountAndUser账户 + 用户 ID(内部使用)AuthenticatorDescription已注册认证器的元数据AccountAuthenticatorResponse向服务返回结果的回调通道32.1.5 账户可见性Android 8.0 + 引入账户可见性机制,管控应用可以看到哪些账户。替代旧版粗放的GET_ACCOUNTS权限模型。可见性等级常量含义不可见VISIBILITY_NOT_VISIBLE应用看不到该账户用户托管可见VISIBILITY_USER_MANAGED_VISIBLE用户授权后才可见可见VISIBILITY_VISIBLE对该应用始终可见未定义VISIBILITY_UNDEFINED回退到认证器默认策略// 设置指定账户+包名的可见性 accountManager.setAccountVisibility( account, "com.example.app", AccountManager.VISIBILITY_VISIBLE ); // 查询可见性 int visibility = accountManager.getAccountVisibility( account, "com.example.app" );32.1.6 AccountManagerFuture — 异步结果模式绝大多数AccountManager方法返回AccountManagerFutureBundle,封装一次异步操作。源码:frameworks/base/core/java/android/accounts/AccountManagerFuture.javaframeworks/base/core/java/android/accounts/AccountManagerCallback.java// 回调方式使用 accountManager.getAuthToken( account, "oauth2:email", null, activity, new AccountManagerCallbackBundle() { @Override public void run(AccountManagerFutureBundle future) { try { Bundle result = future.getResult(); String token = result.getString(AccountManager.KEY_AUTHTOKEN); // 使用令牌 } catch (AuthenticatorException | OperationCanceledException | IOException e) { // 处理异常 } } }, handler ); // 阻塞调用(禁止在UI线程执行) Bundle result = accountManager.getAuthToken( account, "oauth2:email", null, activity, null, // 不使用回调 null // 不使用handler ).getResult(); // 阻塞直至完成 String token = result.getString(AccountManager.KEY_AUTHTOKEN);结果 Bundle 使用如下标准常量键:常量键类型说明KEY_ACCOUNT_NAMEString账户名KEY_ACCOUNT_TYPEString账户类型KEY_AUTHTOKENString身份令牌KEY_INTENTIntent需要拉起用户交互的 UIKEY_BOOLEAN_RESULTboolean布尔返回结果(hasFeatures 等)KEY_ERROR_CODEint认证器返回错误码KEY_ERROR_MESSAGEString认证器返回错误信息KEY_USERDATABundle账户附属用户数据32.1.7 账户变更监听器应用可注册监听,接收账户变更通知:// 注册账户变更监听 accountManager.addOnAccountsUpdatedListener( new OnAccountsUpdateListener() { @Override public void onAccountsUpdated(Account[] accounts) { // 账户新增、删除、重命名时触发 for (Account account : accounts) { Log.i(TAG, "Account: " + account.name + " (" + account.type + ")"); } } }, handler, // 回调执行Handler true // updateImmediately — 立即回调返回当前账户列表 ); // 按类型过滤的监听器(API 26+) accountManager.addOnAccountsUpdatedListener( listener, handler, true, new String[]{"com.google"} // 仅接收Google账户变更通知 );内部实现:AccountManagerService为每个用户维护RemoteCallbackListIAccountManagerResponse,账户发生变更时分发更新。32.1.8 账户 Intent 广播账户新增、删除、修改时,AccountManagerService发送如下广播:广播常量触发条件AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION账户新增 / 删除AccountManager.ACTION_ACCOUNT_REMOVED特定账户被删除(定向广播)AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED账户可见性变更应用(尤其同步适配器)依靠广播感知账户变化,无需轮询。32.2 AccountManagerService32.2.1 服务注册AccountManagerService是 system_server 启动阶段初始化的系统服务,实现IAccountManagerAIDL 接口。源码:frameworks/base/services/core/java/com/android/server/accounts/AccountManagerService.java作为SystemService生命周期组件完成注册:// SystemServer中简化的注册流程 public final class AccountManagerService extends IAccountManager.Stub implements RegisteredServicesCacheListenerAuthenticatorDescription { public static class Lifecycle extends SystemService { private AccountManagerService mService; @Override public void onStart() { mService = new AccountManagerService(getContext()); publishBinderService(Context.ACCOUNT_SERVICE, mService); } @Override public void onUserUnlocking(TargetUser user) { mService.onUserUnlocked(user); } } }32.2.2 按用户隔离的账户存储AccountManagerService为 Android 每一个用户维护独立账户数据。每个用户下存在两套数据库:DE(设备加密)与 CE(凭证加密)存储区分遵循 Android Direct Boot 直接启动模型:存储可用时机存储内容DE(accounts_de.db)设备开机完成后账户名、类型、授权、可见性CE(accounts_ce.db)用户解锁之后密码、身份令牌、用户附属数据源码:frameworks/base/services/core/java/com/android/server/accounts/AccountsDb.java32.2.3 AccountsDb 数据表结构AccountsDb封装 SQLite 数据库操作。核心表:源码:frameworks/base/services/core/java/com/android/server/accounts/AccountsDb.javaCE 数据库表:表字段用途accounts_id, name, type, password, previous_name, last_password_entry_time_millis_epoch账户核心存储authtokens_id, accounts_id, type, authtoken缓存身份令牌extras_id, accounts_id, key, value账户键值对附属数据DE 数据库表:表字段用途accounts_id, name, type账户列表(不含凭证)grantsaccounts_id, auth_token_type, uid按 UID 划分的令牌访问授权visibilityaccounts_id, _package, value按包名的账户可见性shared_accounts_id, name, type跨配置文件共享账户metakey, value服务元数据debug_table各类调试字段调试、审计信息32.2.4 UserAccounts — 用户状态对象AccountManagerService内部为每一个用户持有UserAccounts对象:// 取自AccountManagerService.java简化代码 static class UserAccounts { final int userId; final AccountsDb accountsDb; // 数据库访问对象 final HashMapPairPairAccount, String, Integer, /* ... */ credentialsPermissionNotificationIds; // 待处理权限通知 final HashMapAccount, HashMapString, String userDataC
返回列表