構建多用戶感知應用程序

當一個設備支持多個用戶時,它的應用程序必須知道這些不同的用戶。

某些應用程序需要一些組件作為單例運行,並且可以接受來自任何用戶的請求。目前只有系統應用可以使用此功能。

該設施:

  • 節約資源
  • 仲裁用戶之間的一個或多個共享資源
  • 通過使用單個服務器連接減少網絡開銷

請參閱下圖,了解具有多個用戶的權限流的描述。

多用戶權限流程

圖 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()來禁用整個應用程序。