本开发者指南介绍了如何将专用设备锁定到单个应用或一组应用。如果您是企业移动管理 (EMM) 开发人员或解决方案集成商,请阅读本指南以将锁定任务模式添加到您的解决方案。
概述
Android 可以以沉浸式、类似信息亭的方式运行任务,称为锁定任务模式。如果您正在开发信息亭应用程序或启动器以呈现应用集合,则可能会使用锁定任务模式。当系统在锁定任务模式下运行时,设备用户通常无法查看通知、访问未列入允许列表的应用或返回主屏幕(除非主屏幕已列入允许列表)。
仅当系统处于锁定任务模式时,已由设备策略控制器 (DPC) 列入允许列表的应用才能运行。应用已列入允许列表,因为使用设备的人并非总是可以退出锁定任务模式。
您如何结合为锁定任务模式列入允许列表的应用和列入允许列表的 DPC 将取决于您要解决的问题。以下是一些示例
- 一个将信息亭(用于呈现内容)和小型 DPC(用于将自身列入锁定任务模式的允许列表)结合在一起的单个应用包。
- 作为企业移动管理解决方案一部分的 DPC,在锁定任务模式下启动客户的移动应用。
可用性
系统可以在 Android 5.0 或更高版本中以锁定任务模式运行。表 1 显示了哪些版本的 Android 支持按用户列入应用的允许列表。
Android 版本 | DPC 管理 | 备注 |
---|---|---|
Android 5.0(API 级别 21)或更高版本 | 完全托管设备 | |
Android 8.0(API 级别 26)或更高版本 | 关联的辅助用户 | 辅助用户必须与主要用户关联。请参阅 多用户 概述。 |
Android 9.0(API 级别 28)或更高版本 | 辅助用户 |
在 Android 9.0 或更高版本中,DPC 可以将任何应用的活动启动到锁定任务模式。在早期版本中,应用必须已经支持以锁定任务模式启动自己的活动。
列入应用的允许列表
在应用可以在锁定任务模式下使用之前,DPC 必须将其列入允许列表。调用 DevicePolicyManager.setLockTaskPackages()
以将应用列入锁定任务模式的允许列表,如下例所示
Kotlin
// Allowlist two apps. private val KIOSK_PACKAGE = "com.example.kiosk" private val PLAYER_PACKAGE = "com.example.player" private val APP_PACKAGES = arrayOf(KIOSK_PACKAGE, PLAYER_PACKAGE) // ... val context = context val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager val adminName = getComponentName(context) dpm.setLockTaskPackages(adminName, APP_PACKAGES)
Java
// Allowlist two apps. private static final String KIOSK_PACKAGE = "com.example.kiosk"; private static final String PLAYER_PACKAGE = "com.example.player"; private static final String[] APP_PACKAGES = {KIOSK_PACKAGE, PLAYER_PACKAGE}; // ... Context context = getContext(); DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); ComponentName adminName = getComponentName(context); dpm.setLockTaskPackages(adminName, APP_PACKAGES);
要查找以前已列入锁定任务模式的允许列表的应用,DPC 可以调用 DevicePolicyManager.getLockTaskPackages()
。其他应用可以调用 DevicePolicyManager.isLockTaskPermitted()
以确认应用包是否支持锁定任务模式。
启动锁定任务模式
在 Android 9.0(API 级别 28)或更高版本中,您可以以锁定任务模式启动其他应用的活动。如果活动已经在前台或后台运行,则需要重新启动活动。调用 ActivityOptions.setLockTaskEnabled()
并提供这些选项以启动活动。以下代码片段显示了一种执行此操作的方法
Kotlin
// Set an option to turn on lock task mode when starting the activity. val options = ActivityOptions.makeBasic() options.setLockTaskEnabled(true) // Start our kiosk app's main activity with our lock task mode option. val packageManager = context.packageManager val launchIntent = packageManager.getLaunchIntentForPackage(KIOSK_PACKAGE) if (launchIntent != null) { context.startActivity(launchIntent, options.toBundle()) }
Java
// Set an option to turn on lock task mode when starting the activity. ActivityOptions options = ActivityOptions.makeBasic(); options.setLockTaskEnabled(true); // Start our kiosk app's main activity with our lock task mode option. PackageManager packageManager = context.getPackageManager(); Intent launchIntent = packageManager.getLaunchIntentForPackage(KIOSK_PACKAGE); if (launchIntent != null) { context.startActivity(launchIntent, options.toBundle()); }
在 Android 9.0 之前的版本中,应用通过调用 Activity.startLockTask()
以锁定任务模式启动自己的活动。要调用此方法,活动必须在前台运行(请参阅 活动生命周期概念),因此我们建议在 onResume()
方法(Activity
或 Fragment
)中调用。以下是如何调用 startLockTask()
的方法
Kotlin
// In our Fragment subclass. override fun onResume() { super.onResume() // First, confirm that this package is allowlisted to run in lock task mode. if (dpm.isLockTaskPermitted(context.packageName)) { activity.startLockTask() } else { // Because the package isn't allowlisted, calling startLockTask() here // would put the activity into screen pinning mode. } }
Java
// In our Fragment subclass. @Override public void onResume() { super.onResume(); // First, confirm that this package is allowlisted to run in lock task mode. if (dpm.isLockTaskPermitted(context.getPackageName())) { getActivity().startLockTask(); } else { // Because the package isn't allowlisted, calling startLockTask() here // would put the activity into screen pinning mode. } }
当设备已锁定时,请勿启动锁定任务模式,因为用户可能无法解锁设备。您可以调用 KeyguardManager
方法以了解设备是否已锁定,并使用 Activity
生命周期回调(例如,解锁后调用的 onResume()
)以启动锁定任务模式。
处于锁定任务模式的应用可以启动新的 Activity,只要该 Activity 不启动新的任务即可——除了启动白名单应用的任务。要了解任务与 Activity 之间的关系,请阅读指南 了解任务和返回栈。
或者,您可以在 应用清单文件 中声明 Activity 在系统处于锁定任务模式时应如何运行。要让系统在锁定任务模式下自动运行您的 Activity,请将 android:lockTaskMode
属性设置为 if_whitelisted
,如下例所示
<activity
android:name=".MainActivity"
android:lockTaskMode="if_whitelisted">
<!-- ... -->
</activity>
您可以通过阅读 lockTaskMode
参考了解有关在应用清单文件中声明选项的更多信息。
停止锁定任务模式
DPC 可以通过从白名单中删除应用包来远程停止锁定任务模式。在 Android 6.0(API 级别 23)或更高版本中调用 DevicePolicyManager.setLockTaskPackages()
,并从白名单数组中省略包名。更新白名单后,应用将返回到栈中的上一个任务。
如果 Activity 之前调用过 startLockTask()
,则该 Activity 可以调用 Activity.stopLockTask()
来停止锁定任务模式。此方法仅适用于启动锁定任务模式的 Activity。
生命周期回调
您的 DPC 可能会发现了解应用(在同一用户下运行)何时进入和退出锁定任务模式很有用。要接收回调,请在 DPC 的 DeviceAdminReceiver
子类中覆盖以下回调方法
onLockTaskModeEntering()
- 应用进入锁定任务模式后调用。您可以从
pkg
参数获取应用的包名。 onLockTaskModeExiting()
- 应用退出锁定任务模式后调用。此回调不会接收有关应用的信息。
如果将另一个应用启动到锁定任务模式,则需要在自己的应用中跟踪运行状态。要检查当前应用是否正在锁定任务模式下运行,请使用 ActivityManager
上的方法,如下例所示
Kotlin
// Check if this app is in lock task mode. Screen pinning doesn't count. var isLockTaskModeRunning = false val activityManager = context .getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { isLockTaskModeRunning = activityManager.lockTaskModeState == ActivityManager.LOCK_TASK_MODE_LOCKED } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Deprecated in API level 23. isLockTaskModeRunning = activityManager.isInLockTaskMode } if (isLockTaskModeRunning) { // Show the exit button ... }
Java
// Check if this app is in lock task mode. Screen pinning doesn't count. boolean isLockTaskModeRunning = false; ActivityManager activityManager = (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { isLockTaskModeRunning = activityManager.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED; } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Deprecated in API level 23. isLockTaskModeRunning = activityManager.isInLockTaskMode(); } if (isLockTaskModeRunning) { // Show the exit button ... }
自定义 UI
当应用在锁定任务模式下运行时,系统用户界面 (UI) 会发生以下变化
- 状态栏为空白,隐藏通知和系统信息。
- 隐藏主页和概览按钮。
- 其他应用无法启动新的 Activity。
- 锁定屏幕(如果已设置)被禁用。
在 Android 9.0 或更高版本中,启用锁定任务模式后,您的 DPC 可以启用设备上的某些系统 UI 功能——对创建自定义启动器的开发者很有用。调用 DevicePolicyManager.setLockTaskFeatures()
,如下面的代码片段所示
Kotlin
// Enable the Home and Overview buttons so that our custom launcher can respond // using our custom activities. Implicitly disables all other features. dpm.setLockTaskFeatures( adminName, DevicePolicyManager.LOCK_TASK_FEATURE_HOME or DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW)
Java
// Enable the Home and Overview buttons so that our custom launcher can respond // using our custom activities. Implicitly disables all other features. dpm.setLockTaskFeatures(adminName, DevicePolicyManager.LOCK_TASK_FEATURE_HOME | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW);
系统会禁用您未包含在 flags
参数中的任何功能。启用的 UI 功能在启动锁定任务模式之间持续存在。如果设备已处于锁定任务模式,则对锁定任务功能所做的任何更改都会立即显示。表 2 描述了您可以自定义的 UI 功能。
系统 UI 功能 | 描述 |
---|---|
LOCK_TASK_FEATURE_HOME
|
显示主页按钮。为自定义启动器启用——除非您将默认的 Android 启动器列入白名单,否则点击启用的主页按钮没有任何操作。 |
LOCK_TASK_FEATURE_OVERVIEW
|
显示概览按钮(点击此按钮会打开 最近使用的应用 屏幕)。如果启用此按钮,则还必须启用主页按钮。 |
LOCK_TASK_FEATURE_GLOBAL_ACTIONS
|
启用全局操作对话框,该对话框在长按电源按钮时显示。当 setLockTaskFeatures() 未被调用时启用的唯一功能。如果禁用此对话框,用户通常无法关闭设备。 |
LOCK_TASK_FEATURE_NOTIFICATIONS
|
启用所有应用的通知。这会在状态栏中显示通知图标、悬浮式通知和可展开的通知栏。如果启用此按钮,则还必须启用主页按钮。点击通知操作和打开新面板的按钮在锁定任务模式下不起作用。 |
LOCK_TASK_FEATURE_SYSTEM_INFO
|
启用状态栏的系统信息区域,其中包含连接性、电池以及声音和振动选项等指示器。 |
LOCK_TASK_FEATURE_KEYGUARD
|
启用设备上可能设置的任何锁定屏幕。通常不适用于具有公共用户的设备,例如信息亭或数字标牌。 |
LOCK_TASK_FEATURE_NONE
|
禁用上面列出的所有系统 UI 功能。 |
DPC 可以调用 DevicePolicyManager.getLockTaskFeatures()
以获取启用锁定任务模式时设备上可用功能的列表。当设备退出锁定任务模式时,用户界面将恢复到现有设备策略规定的状态。
阻止窗口和叠加层
当应用在锁定任务模式下运行时,其他应用和后台服务可以创建新的窗口,Android 会将这些窗口显示在锁定任务模式下应用的前面。应用和服务创建这些窗口以向使用设备的人员显示吐司、对话框和叠加层。您的 DPC 可以通过添加 DISALLOW_CREATE_WINDOWS
用户限制来阻止这些窗口。以下示例显示了您如何在 onLockTaskModeEntering()
回调中执行此操作
Kotlin
// Called just after entering lock task mode. override fun onLockTaskModeEntering(context: Context, intent: Intent) { val dpm = getManager(context) val admin = getWho(context) dpm.addUserRestriction(admin, UserManager.DISALLOW_CREATE_WINDOWS) }
Java
// Called just after entering lock task mode. public void onLockTaskModeEntering(Context context, Intent intent) { DevicePolicyManager dpm = getManager(context); ComponentName admin = getWho(context); dpm.addUserRestriction(admin, UserManager.DISALLOW_CREATE_WINDOWS); }
您的 DPC 可以在设备退出锁定任务模式时删除用户限制。
其他资源
要了解有关专用设备的更多信息,请阅读以下文档