一、 简介
本文基于Android Q,介绍安卓原生的多用户功能适配相关内容,供开发者学习参考。
多用户不同于多进程、多线程,前者是本地数据的差异,后两者是运行时内存数据的差异。
因此多用户适配,主要是适配本地保存的数据相关的IO操作,以及监听多用户切换的事件,包括用户创建、用户切换、用户展示等。
二、 调试命令
- 获取所有用户
adb shell pm list users
- 创建用户
普通用户:adb shell pm create-user test
访客用户:adb shell pm create-user --guest test
- 切换用户
adb shell am switch-user [userId]
- 获取当前用户
adb shell am get-current-user
- 删除用户
adb shell pm remove-user [userId]
- 获取所有用户信息
adb shell dumpsys user
- 获取同时运行最大用户数
adb shell pm get-max-running-users
- 获取最大用户数
adb shell pm get-max-users
三、 大致原理
- data目录下的两个user目录下,是存放每个用户的应用数据的,通过userId进行区分。
- 当新建用户时,会先新建userId命名的目录,然后拷贝系统用户(userId等于0)的应用数据到新用户,具体是通过安装还是复制内容,待读者去分析。
- 这一过程仅在新建用户时会执行,比较耗时,所以会很卡。
- 新建用户结束后,是切换用户过程,该过程下,原先用户的进程会进入冻结状态或杀死状态,此时用户是locked状态。对于独立应用,会在新用户下创建新的进程,因此无需额外处理,自然适配;对于部分系统应用,会保留进程,仅切换userId,因此需要主动适配。这个过程中,应用不得向本地本件进行IO操作,否则会导致异常,例如:
java.lang.IllegalStateException: SharedPreferences in credential encrypted storage are not available until after user is unlocked
- 切换用户之后,才会展示用户,即系统及应用整体初始化完成,此时用户才是unlocked状态,应用可以进行本地文件IO操作。
四、 常用方法
4.1 UserId和UserHandle
UserHandle是UserId的包装类,UserId有几个常用常量,且各自有对应的包装类:
- USER_ALL = -1:非确切id,标识任意用户
- USER_CURRENT = -2:非确切id,标识当前用户
- USER_SYSTEM = 0:确切id,标识系统用户,即主用户、机主
- 其他大于1的值:确切id,标识子用户,包括访客等
4.2 获取UserId
(1) Context
int userId