十二、多用户与工作资料:多用户场景下的升级兼容性,Managed Profile的API变化
多用户场景,说白了就是一台设备给好几个人用。Android从很早开始就支持这个功能了,但真正让开发者头疼的,是工作资料(Managed Profile)这个玩意儿。
我个人习惯把工作资料理解成「手机里的另一个小手机」。它跟主用户共享同一个系统,但有自己的用户ID、存储空间、应用列表。嗯,这里要注意——升级的时候,这个「小手机」的兼容性问题往往比主用户更隐蔽。
多用户架构的核心概念
先理清楚几个基本概念,不然后面代码你看着会晕。
- 系统用户(User 0):系统级用户,所有用户共享的系统服务跑在这里
- 主用户(Primary User):设备主人,通常是User 10
- 工作资料(Managed Profile):企业管理的用户,关联到主用户,User ID通常是主用户+10
- 访客用户(Guest User):临时用户,用完就删
我在项目中遇到过一个问题:某次OTA升级后,工作资料里的应用全部闪退。查了半天,发现是升级后系统服务里某个Binder接口变了,但工作资料里的应用还在用旧的AIDL文件。你想想看,这多坑。
升级时多用户状态的变化
系统升级时,多用户的状态变化是有严格顺序的。我建议你记住这个流程:
- 系统先冻结所有非系统用户
- 升级系统服务(System Server)
- 更新系统用户的应用
- 逐个解冻其他用户
- 每个用户独立完成应用更新
为什么会这样?因为系统服务只有一个实例,它必须先升级完,其他用户才能正常工作。我曾经遇到一个Case:某个厂商在升级时跳过了步骤4,结果工作资料里的应用全部拿不到正确的系统服务版本,直接崩溃。
Managed Profile的API变化
Android每个大版本都会调整Managed Profile相关的API。我整理了一份比较关键的变更表:
| Android版本 | API变化 | 影响范围 |
|---|---|---|
| Android 10 | UserManager.isManagedProfile() 行为变更 | 所有检查用户类型的代码 |
| Android 11 | WorkProfile 的跨用户Intent限制加强 | 启动Activity、发送广播 |
| Android 12 | 新增 getProfileParent() 的返回值处理 | 获取父用户信息的逻辑 |
| Android 13 | Managed Profile 的存储隔离策略更新 | 文件访问、ContentProvider |
| Android 14 | 跨用户广播的权限检查收紧 | 所有跨用户通信 |
说白了,每次升级你都得重新检查一遍这些API。我个人习惯是在升级前写一个自动化测试脚本,专门跑这些API的兼容性。
跨用户通信的兼容性处理
这是多用户场景下最容易出问题的地方。工作资料和主用户之间怎么通信?我直接给代码:
// 检查当前是否在工作资料中
UserManager um = context.getSystemService(UserManager.class);
if (um != null && um.isManagedProfile()) {
// 获取父用户(主用户)
UserHandle parentUser = um.getProfileParent(Process.myUserHandle());
if (parentUser != null) {
// 跨用户发送广播
Intent intent = new Intent("com.example.CROSS_USER_ACTION");
intent.setPackage("com.example.app");
// Android 12+ 必须指定包名
context.sendBroadcastAsUser(intent, parentUser);
}
}
// 接收端需要声明跨用户权限
// AndroidManifest.xml 中:
// <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
嗯,这里要注意。Android 12之后,跨用户广播的权限检查变得非常严格。我曾经遇到一个坑:某个应用在Android 11上能正常跨用户通信,升级到Android 12后直接收不到广播。查了半天,发现是sendBroadcastAsUser在Android 12上要求目标必须显式指定包名。
android:multiprocess="true"。
工作资料的应用升级策略
工作资料里的应用升级,跟主用户不太一样。我总结了几条经验:
- 版本号必须一致:主用户和工作资料里的同一个应用,版本号必须相同。否则系统会拒绝安装
- 签名必须一致:这个不用多说,但要注意工作资料可能使用不同的签名证书
- 数据迁移要小心:升级时工作资料的应用数据不会自动迁移,你得自己处理
我记得有一次,某个企业应用在主用户上升级到了v2.0,但工作资料里还是v1.0。结果工作资料里的应用启动时,发现数据库结构变了,直接崩溃。这个问题的根源就是版本号不一致导致的。
SVG:多用户升级兼容性核心逻辑
实际项目中的避坑指南
最后,我把自己踩过的坑整理一下,希望能帮你少走弯路。
- 不要假设工作资料一定存在:很多设备根本没有配置工作资料,你的代码必须能优雅处理
null - 跨用户文件共享要谨慎:Android 13之后,工作资料的存储路径变了,旧路径可能无法访问
- 升级后检查用户状态:我建议在
onUpgrade回调里主动调用UserManager.getUserProfiles(),确认所有用户都正常 - 测试要覆盖多用户场景:别只在主用户下测试,工作资料下的行为可能完全不同
我曾经在一个项目里,因为没检查工作资料是否存在,直接调用了getProfileParent(),结果在非工作资料设备上返回了null,导致空指针异常。嗯,从那以后我养成了一个习惯:所有跟多用户相关的API调用,前面都加一个null检查。
多用户兼容性这块,说白了就是「别想当然」。你以为工作资料跟主用户一样,其实处处是坑。保持敬畏心,多测试,多验证,才能平稳升级。