构建多用户感知的应用程序

当设备支持多个用户时,其应用程序必须了解这些不同的用户。

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

该设施:

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

请参阅下图,了解多个用户的权限流的描述。

多用户权限流程

图 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 来启动特定用户的服务、活动和广播:
    • 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 的回调,这些 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()禁用整个应用程序。