构建可感知多用户的应用

对于支持多用户的设备,设备上的应用必须能够感知不同的用户。

某些应用需要将一些组件作为单例运行,并且可接受来自任意用户的请求。目前只有系统应用可以使用此功能。

这类设备具有以下优势:

  • 节约资源
  • 判定各个用户之间的一个或多个共享资源
  • 通过使用单个服务器连接减少网络开销

有关多用户权限流程的描述,请参见下图。

多用户权限流程

图 1. 多用户权限

启用单例组件

要将应用识别为单例,请将 android:singleUser=”true” 添加至 Android 清单中的服务或提供程序。

在仅以用户 0 的身份运行的进程中,系统会将该组件实例化。所有连接到该提供程序或服务的用户请求都将按指定路径发送到用户 0 的进程中。如果该组件是应用中的唯一组件,则只运行一个应用实例。

软件包中的活动仍将在每个用户各自的进程中启动,并且 UID 处于相应用户的 UID 范围内(例如 1010034)。

与用户互动

设置权限

需要下列权限:

INTERACT_ACROSS_USERS (signature|system)
INTERACT_ACROSS_USERS_FULL (signature)

使用 API

使用下列 API,确保应用能够感知多个用户。

  1. 从传入的 Binder 调用中提取用户句柄:
    • int userHandle = UserHandle.getCallingUserId()
  2. 使用受保护的全新 API 启动特定用户的服务、Activity 和广播:
    • Context.startActivityAsUser(Intent, UserHandle)
    • Context.bindServiceAsUser(Intent, …, UserHandle)
    • Context.sendBroadcastAsUser(Intent, … , UserHandle)
    • Context.startServiceAsUser(Intent, …, UserHandle) UserHandle 可能是显式用户或特殊句柄之一:UserHandle.CURRENTUserHandle.ALLCURRENT 表示当前位于前台的用户。如果您想向所有用户发送广播,则可以使用 ALL
  3. 要与您自己应用中的组件通信,请使用: (INTERACT_ACROSS_USERS) 要与其他应用中的组件通信,请使用: (INTERACT_ACROSS_USERS_FULL)
  4. 您可能需要创建代理组件,这些代理组件在用户进程中运行,之后会访问用户 0 中的 singleUser 组件。
  5. 使用新的 UserManager 系统服务查询用户及其句柄:
    • UserManager.getUsers()
    • UserManager.getUserInfo()
    • UserManager.supportsMultipleUsers()
    • UserManager.getUserSerialNumber(int userHandle) - 与用户句柄对应的不可再循环数字。
    • UserManager.getUserHandle(int serialNumber)
    • UserManager.getUserProfiles() - 返回用户本人个人资料和托管个人资料的集合(如有)。
  6. 注册即可借助 ContentObserver、PackageMonitor 和 BroadcastReceiver 上的新 API 监听特定或所有用户以及回调(可提供与回调发起用户相关的其他信息)。

多个用户或资料中的服务

并非所有服务都需要在其他用户或工作资料中运行实例。如果您的系统服务只需要以用户 0 的身份运行,则在以其他用户的身份运行时停用该服务的组件,以帮助保留资源。以下示例显示了如何在服务的入口点执行此操作:

// Add on all entry points such as boot_completed or other manifest-listed receivers and providers
if (!UserManager.isSystemUser()) {
    // Disable the service
    ComponentName targetServiceName = new ComponentName(this, TargetService.class);
    context.getPackageManager().setComponentEnabledSetting(
        targetServiceName, COMPONENT_ENABLED_STATE_DISABLED, 0);
}

该示例还可以使用 PackageManager.setApplicationEnabledSetting() 来停用整个应用。