17、多用户与账户系统:多用户管理、用户切换与配置文件、账户同步策略

多用户系统,说白了就是让一辆车能服务好一家人。你想想看,爸爸喜欢导航播报用林志玲的声音,妈妈习惯把座椅调到最低,孩子只想看动画片——没有多用户,这些需求根本没法同时满足。

我在做第一代车载系统时,遇到过最头疼的问题就是:用户切换后,蓝牙连接断了,WiFi密码丢了,甚至导航收藏夹全没了。嗯,这背后其实就是多用户架构没设计好。今天我们就来彻底搞懂它。

17.1 多用户管理:Android Automotive 的用户模型

Android Automotive 的多用户,和手机上的多用户不太一样。手机上的多用户更像「访客模式」,而车载的多用户是「驾驶员专属空间」。每个用户都有自己的设置、应用数据、账户信息。

核心概念就三个:

  • UserHandle:每个用户的唯一标识。系统通过它来隔离数据。
  • UserManager:管理用户的创建、删除、切换。这是多用户的总闸。
  • UserInfo:描述用户的信息,比如用户名、头像、是否是管理员。

我习惯把 UserManager 看作「酒店前台」,UserInfo 是「房卡」,UserHandle 是「房间号」。前台告诉你哪个房间有人,房卡决定你能进哪个房间,房间号决定了你的东西放在哪。

关键点:车载系统通常有 3 种用户类型:

  • 系统用户(System User):User 0,系统服务跑在这里,普通用户看不到。
  • 管理员用户(Admin User):可以管理其他用户,通常对应车主。
  • 普通用户(Secondary User):家庭成员或临时驾驶员。

创建用户的代码其实不复杂,但权限要求很高:

// 获取 UserManager 实例
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);

// 创建新用户(需要 MANAGE_USERS 权限)
UserHandle newUser = userManager.createUser("张三", 0); // 0 表示普通用户

// 设置用户头像
userManager.setUserIcon(newUser, bitmap);

// 删除用户
userManager.removeUser(newUser);

我在项目中遇到过一个问题:创建用户时如果传入了错误的 flag,会导致新用户无法使用某些系统功能。比如忘记加 FLAG_EPHEMERAL,临时用户的数据就不会被自动清理。嗯,这个坑踩过一次就记住了。

17.2 用户切换与配置文件:切换背后的机制

用户切换,听起来就是点一下头像换个账号。但底层发生的事情,比你想象的多得多。

切换流程大致是这样的:

  1. 系统收到切换请求,调用 UserManager.switchUser()
  2. AMS(ActivityManagerService)开始冻结当前用户的所有进程。
  3. 系统切换到目标用户的 UserData 目录。
  4. 启动目标用户的 Launcher 和系统服务。
  5. 恢复目标用户的应用进程。

这里面最耗时的,其实是第 3 步——切换数据目录。每个用户的数据都存放在 /data/user/<userId>/ 下。切换用户,本质上就是切换这个目录的挂载点。

我的建议:用户切换时,不要做太多「清理工作」。我曾经在切换回调里做了大量数据库迁移,结果切换一次要 8 秒。后来改成懒加载,用户点开应用时才迁移,体验好多了。

配置文件这块,Android Automotive 用的是 Profile 机制。每个用户可以有多个 profile,比如「工作模式」和「个人模式」。但车载场景下,profile 更多用于区分「驾驶模式」和「停车模式」。

// 获取当前用户的配置文件
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
UserHandle myUser = Process.myUserHandle();

// 获取该用户的所有 profile
List<UserHandle> profiles = userManager.getUserProfiles();

// 检查当前是否是 managed profile
boolean isManagedProfile = userManager.isManagedProfile();

你想想看,为什么需要 profile?因为同一个驾驶员,开车时和停车时需要的应用完全不同。开车时只能看导航和音乐,停车时可以看视频、玩游戏。用 profile 来隔离,比用多用户更轻量。

注意:profile 切换不会触发用户切换的完整流程,所以速度更快。但 profile 之间的数据隔离不如多用户彻底。如果你的应用需要完全隔离的数据,请使用多用户,而不是 profile。

17.3 账户同步策略:让数据跟着用户走

多用户搞定了,账户系统也得跟上。否则换个用户,Spotify 账号还得重新登录,那体验就太差了。

Android 的账户系统核心是 AccountManager。每个用户可以绑定多个账户,比如 Google 账号、百度账号、车厂自己的账号。

账户同步,说白了就是:用户 A 登录了 Spotify,切换到用户 B 后,用户 B 的 Spotify 应该自动登录自己的账号,而不是沿用用户 A 的。

实现这个逻辑,需要做两件事:

  • 账户隔离:每个用户的账户数据存放在自己的 UserData 目录下。
  • 同步适配器:系统通过 SyncAdapter 来同步账户数据。
// 获取当前用户的 AccountManager
AccountManager accountManager = AccountManager.get(context);

// 获取当前用户的所有账户
Account[] accounts = accountManager.getAccounts();

// 添加账户
Account account = new Account("zhangsan@example.com", "com.example.account");
accountManager.addAccountExplicitly(account, "password", null);

// 设置同步
ContentResolver.setSyncAutomatically(account, "com.example.provider", true);
ContentResolver.addPeriodicSync(account, "com.example.provider", new Bundle(), 3600);

我在项目中遇到过最典型的坑是:账户同步时机。用户切换后,如果立即触发同步,网络可能还没准备好。我建议的做法是:监听用户切换完成广播,延迟 3-5 秒再启动同步。

同步策略总结

策略 说明 适用场景
立即同步 用户切换后立即同步 关键数据,如导航收藏
延迟同步 等待 3-5 秒后同步 非关键数据,如音乐播放列表
按需同步 用户打开应用时才同步 第三方应用数据
定时同步 每隔一段时间同步一次 后台数据,如天气信息

还有一个容易被忽略的点:账户删除。当管理员删除一个用户时,该用户的所有账户也应该被清理。否则下次创建同名用户时,可能会残留旧的账户数据。

// 监听用户删除事件
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
userManager.addUserStatusListener(new UserManager.UserStatusListener() {
    @Override
    public void onUserRemoved(int userId) {
        // 清理该用户的所有账户
        AccountManager accountManager = AccountManager.get(context);
        Account[] accounts = accountManager.getAccountsForUser(userId);
        for (Account account : accounts) {
            accountManager.removeAccountExplicitly(account);
        }
    }
});

嗯,这里要注意:getAccountsForUser() 这个 API 在 Android 10 之后才稳定可用。如果你的系统版本较低,可能需要通过反射或者 ContentProvider 来获取。

17.4 多用户架构的整体视图

说了这么多,我们来画一张图,把多用户、用户切换、账户同步串起来。

车载多用户系统架构图 用户管理层 UserManager UserHandle UserInfo 创建/删除/切换 数据隔离层 /data/user/<userId>/ 应用数据隔离 配置文件隔离 存储空间隔离 账户同步层 AccountManager SyncAdapter 账户隔离 同步策略 切换触发 账户绑定 同步完成通知 应用层 Launcher | 设置 | 导航 | 音乐 | 视频 请求切换 获取账户 用户切换流程:应用请求 → 用户管理层切换 → 数据隔离层切换目录 → 账户同步层恢复账户 关键点:用户切换不丢失数据,账户同步不泄露隐私 每个用户拥有独立的 UserData 目录和 Account 空间

这张图把三个核心层的关系讲清楚了。用户管理层负责「谁在用」,数据隔离层负责「数据在哪」,账户同步层负责「账号怎么跟」。三层协作,才能实现流畅的多用户体验。

个人经验:我建议你在设计多用户系统时,先把「用户切换的边界条件」列清楚。比如:

  • 用户切换时,正在播放的音乐要不要暂停?
  • 导航中的路线要不要保存?
  • 蓝牙通话中的电话要不要挂断?

这些细节,比技术实现更影响用户体验。我曾经因为没处理好蓝牙通话的切换,被用户投诉了好几次。

好了,多用户与账户系统就讲到这里。核心就三件事:管好用户、隔好数据、同步好账户。下一层我们聊聊更具体的系统服务,但那是后话了。


公众号:蓝海资料掘金营,微信deep3321