本开发者指南介绍了如何将专用设备锁定到单个应用或一组应用。如果您是企业移动管理 (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()
,它在解锁后被调用)来启动锁定任务模式。
处于锁定任务模式下的应用可以启动新的活动,只要该活动不启动新的任务——除了启动已列入白名单的应用的任务。要了解任务与活动之间的关系,请阅读指南 了解任务和后退堆栈。
或者,您可以在 应用清单文件 中声明活动在系统运行于锁定任务模式时应如何运行。要使系统自动以锁定任务模式运行您的活动,请将 android:lockTaskMode
属性设置为 if_whitelisted
,如下例所示
<activity
android:name=".MainActivity"
android:lockTaskMode="if_whitelisted">
<!-- ... -->
</activity>
您可以通过阅读 lockTaskMode
参考来了解更多关于在应用清单文件中声明选项的信息。
停止锁定任务模式
DPC 可以通过从白名单中删除应用包来远程停止锁定任务模式。在 Android 6.0(API 级别 23)或更高版本中,调用 DevicePolicyManager.setLockTaskPackages()
,并将包名称从白名单数组中省略。更新白名单后,应用将返回到堆栈中的前一个任务。
如果活动先前调用了 startLockTask()
,则该活动可以调用 Activity.stopLockTask()
来停止锁定任务模式。此方法仅适用于启动锁定任务模式的活动。
生命周期回调
您的 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) 会发生以下变化
- 状态栏为空,隐藏了通知和系统信息。
- 隐藏了主页和概览按钮。
- 其他应用无法启动新活动。
- 锁定屏幕(如果已设置)已禁用。
在 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 可以在设备退出锁定任务模式时删除用户限制。
其他资源
要了解有关专用设备的更多信息,请阅读以下文档