本开发者指南将介绍如何将专用设备锁定为单个应用或一组应用。如果您是企业移动管理 (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 可以将任何应用的 activity 启动到锁定任务模式中。在早期版本中,应用必须支持在锁定任务模式下启动自己的 activity。
将应用列入许可名单
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)或更高版本中,您可以在锁定任务模式下启动另一个应用的 activity。如果某个 activity 已在前台或后台运行,您需要重新启动该 activity。调用 ActivityOptions.setLockTaskEnabled()
并在启动 activity 时提供这些选项。以下代码段展示了实现此目的的一种方法:
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()
在锁定任务模式下启动自己的 activity。如需调用此方法,activity 必须在前台运行(请参阅 activity 生命周期概念),因此我们建议在 Activity
或 Fragment
的 onResume()
方法中调用。调用 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) 会发生以下变化:
- 状态栏空白,并隐藏了通知和系统信息。
- “首页”按钮和“概览”按钮处于隐藏状态。
- 其他应用无法启动新 activity。
- 锁定屏幕(如果已设置)处于停用状态。
在 Android 9.0 或更高版本中,启用锁定任务模式后,DPC 可在设备上启用某些系统界面功能,这对开发者创建自定义启动器很有用。调用 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
参数中未包含的任何功能。启用的界面功能会在两次启动之间持续到锁定任务模式中。如果设备已处于锁定任务模式,您对锁定任务功能所做的任何更改会立即显示。表 2 介绍了您可以自定义的界面功能。
系统界面功能 | 说明 |
---|---|
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
|
停用上面列出的所有系统界面功能。 |
启用锁定任务模式后,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 可以在设备退出锁定任务模式时移除用户限制。
其他资源
如需详细了解专用设备,请参阅以下文档: