如果您要为企业市场开发应用,可能需要满足组织政策规定的特定要求。受管理的配置(以前称为应用限制)可使组织的 IT 管理员远程为应用指定设置。对于已获得组织批准且部署至工作资料的应用,此功能尤为有用。
例如,某个组织可能会要求已获批准的应用允许 IT 管理员执行以下操作:
- 允许或屏蔽网络浏览器的网址
- 配置是否允许应用通过移动网络或仅通过 Wi-Fi 同步内容
- 配置应用的电子邮件设置
本指南介绍了如何在应用中实现受管理配置设置。如需查看使用受管理配置的示例应用,请参阅 ManagedConfigurations。 如果您是企业移动管理 (EMM) 开发者,请参阅 Android Management API 指南。
注意:出于历史原因,这些配置设置被称为限制,并使用使用此术语的文件和类(例如 RestrictionsManager
)进行实现。不过,这些限制实际上可以实现各种配置选项,而不仅仅是应用功能限制。
远程配置概览
应用定义了可由 IT 管理员远程设置的受管理的配置选项。这些是可由托管式配置提供程序更改的任意设置。如果您的应用在工作资料中运行,IT 管理员可以更改应用的受管理配置。
受管理的配置提供程序是同一设备上运行的另一个应用。 此应用通常由 IT 管理员控制。IT 管理员将配置更改传达给受管理的配置提供程序应用。该应用反过来会更改应用的配置。
如需提供外部管理的配置,请执行以下操作:
- 在应用清单中声明受管理的配置。这样,IT 管理员就可以通过 Google Play API 读取应用的配置。
- 每当应用恢复时,请使用
RestrictionsManager
对象检查当前的受管理配置,并更改应用的界面和行为以符合这些配置。 - 监听
ACTION_APPLICATION_RESTRICTIONS_CHANGED
intent。收到此广播时,请检查RestrictionsManager
以查看当前的受管配置,并对应用的行为进行任何必要的更改。
定义受管理的配置
您的应用可以支持您要定义的任何受管理配置。您可以在托管配置文件中声明应用的托管配置,并在清单中声明配置文件。创建配置文件后,其他应用便可以检查您的应用提供的受管理配置。EMM 合作伙伴可以使用 Google Play API 读取应用的配置。
如需定义应用的远程配置选项,请在清单的
<application>
元素中添加以下元素:
<meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions" />
在应用的 res/xml
目录中创建一个名为 app_restrictions.xml
的文件。RestrictionsManager
的参考文档介绍了该文件的结构。该文件包含一个顶级 <restrictions>
元素,该元素包含与应用的每个配置选项对应的 <restriction>
子元素。
注意:请勿创建经过本地化处理的托管配置文件版本。您的应用只能有一个受管理的配置文件,因此应用在所有语言区域中的配置将保持一致。
在企业环境中,EMM 通常会使用受管理的配置架构为 IT 管理员生成远程控制台,以便管理员远程配置您的应用。
受管配置提供程序可以查询应用,以了解应用的可用配置的详细信息,包括其说明文本。配置提供方和 IT 管理员可以随时更改应用的受管理配置,即使应用未运行也是如此。
例如,假设您的应用可以远程配置为允许或禁止通过移动网络连接下载数据。您的应用可以包含如下所示的 <restriction>
元素:
<?xml version="1.0" encoding="utf-8"?> <restrictions xmlns:android="http://schemas.android.com/apk/res/android"> <restriction android:key="downloadOnCellular" android:title="@string/download_on_cell_title" android:restrictionType="bool" android:description="@string/download_on_cell_description" android:defaultValue="true" /> </restrictions>
您可以使用每个配置的 android:key
属性从受管理的配置软件包中读取其值。因此,每个配置都必须具有唯一的键字符串,并且该字符串不能本地化。必须使用字符串字面量进行指定。
注意:在正式版应用中,android:title
和 android:description
应从本地化资源文件中绘制,如使用资源进行本地化中所述。
应用使用 bundle_array
中的软件包定义限制。
例如,具有多个 VPN 连接选项的应用可以在 bundle
中定义每个 VPN 服务器配置,并将多个软件包组合到软件包数组中:
<?xml version="1.0" encoding="utf-8"?> <restrictions xmlns:android="http://schemas.android.com/apk/res/android" > <restriction android:key="vpn_configuration_list" android:restrictionType="bundle_array"> <restriction android:key="vpn_configuration" android:restrictionType="bundle"> <restriction android:key="vpn_server" android:restrictionType="string"/> <restriction android:key="vpn_username" android:restrictionType="string"/> <restriction android:key="vpn_password" android:restrictionType="string"/> </restriction> </restriction> </restrictions>
表 1 列出了 android:restrictionType
元素支持的类型,RestrictionsManager
和 RestrictionEntry
的参考文档中也记录了这些类型。
表 1. 限制条目类型和用法。
类型 | android:restrictionType | 典型用法 |
---|---|---|
TYPE_BOOLEAN
|
"bool" |
布尔值,true 或 false。 |
TYPE_STRING
|
"string" |
字符串值,例如名称。 |
TYPE_INTEGER
|
"integer" |
一个整数,值介于 MIN_VALUE 和 MAX_VALUE 之间。
|
TYPE_CHOICE
|
"choice" |
从 android:entryValues 中选择的字符串值,通常显示为单选列表。
|
TYPE_MULTI_SELECT
|
"multi-select" |
一个字符串数组,其中包含从 android:entryValues 中选择的值。
用于显示可选择多个条目的多选列表,例如选择要列入许可名单的特定影视内容。
|
TYPE_NULL
|
"hidden" |
隐藏的限制类型。此类型适用于需要传输但不应在界面中向用户显示的信息。存储单个字符串值。 |
TYPE_BUNDLE_ARRAY
|
"bundle_array" |
用于存储限制 bundles 的数组。适用于 Android 6.0(API 级别 23)。
|
注意:android:entryValues
是机器可读的,无法本地化。使用 android:entries
显示可本地化的直观易懂的值。
每个条目都必须在 android:entryValues
中有对应的索引。
检查受管理的配置
当其他应用更改其配置设置时,您的应用不会自动收到通知。不过,您需要在应用启动或恢复时检查受管理的配置,并监听系统 intent 以了解配置在应用运行期间是否发生变化。
如需了解当前配置设置,您的应用会使用 RestrictionsManager
对象。您的应用应在以下时间检查当前的受管配置:
- 在应用启动或恢复时,在其
onResume()
方法中 - 当应用收到配置更改通知时(如监听受管理配置更改中所述)
如需获取 RestrictionsManager
对象,请使用 getActivity()
获取当前 activity,然后调用该 activity 的 Activity.getSystemService()
方法:
Kotlin
var myRestrictionsMgr = activity?.getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager
Java
RestrictionsManager myRestrictionsMgr = (RestrictionsManager) getActivity() .getSystemService(Context.RESTRICTIONS_SERVICE);
创建 RestrictionsManager
后,您可以通过调用其 getApplicationRestrictions()
方法来获取当前配置设置:
Kotlin
var appRestrictions: Bundle = myRestrictionsMgr.applicationRestrictions
Java
Bundle appRestrictions = myRestrictionsMgr.getApplicationRestrictions();
注意:为方便起见,您还可以通过调用 UserManager.getApplicationRestrictions()
来使用 UserManager
提取当前配置。此方法的行为与 RestrictionsManager.getApplicationRestrictions()
完全相同。
getApplicationRestrictions()
方法需要从数据存储空间读取,因此应尽量减少使用。请勿在每次需要了解当前配置时都调用此方法。相反,您应在应用启动或恢复时调用该方法一次,并缓存提取的受管理配置软件包。然后,监听 ACTION_APPLICATION_RESTRICTIONS_CHANGED
intent 以了解配置是否在应用处于活动状态时发生更改,如监听受管理的配置变更中所述。
读取和应用受管配置
getApplicationRestrictions()
方法会返回一个 Bundle
,其中包含已设置的每个配置的键值对。这些值均为 Boolean
、int
、String
和 String[]
类型。创建受管理配置 Bundle
后,您可以使用标准 Bundle
方法检查这些数据类型(例如 getBoolean()
或 getString()
)的当前配置设置。
注意:托管配置 Bundle
针对托管配置提供方明确设置的每个配置包含一个项。不过,仅仅因为您在受管理的配置 XML 文件中定义了默认值,就不能假定软件包中会存在相应配置。
您的应用可以根据当前的受管配置设置采取适当的措施。例如,如果您的应用具有配置,用于指定其是否可以通过移动网络连接下载数据,并且您发现该配置已设置为 false
,则必须停用数据下载,除非设备已连接到 Wi-Fi,如以下示例代码所示:
Kotlin
val appCanUseCellular: Boolean = if (appRestrictions.containsKey("downloadOnCellular")) { appRestrictions.getBoolean("downloadOnCellular") } else { // cellularDefault is a boolean using the restriction's default value cellularDefault } if (!appCanUseCellular) { // ...turn off app's cellular-download functionality // ...show appropriate notices to user }
Java
boolean appCanUseCellular; if (appRestrictions.containsKey("downloadOnCellular")) { appCanUseCellular = appRestrictions.getBoolean("downloadOnCellular"); } else { // cellularDefault is a boolean using the restriction's default value appCanUseCellular = cellularDefault; } if (!appCanUseCellular) { // ...turn off app's cellular-download functionality // ...show appropriate notices to user }
如需应用多个嵌套限制,请将 bundle_array
限制条目读取为 Parcelable
对象的集合,并将其转换为 Bundle
。在此示例中,系统会解析每个 VPN 的配置数据,并将其用于构建服务器连接选项列表:
Kotlin
// VpnConfig is a sample class used store config data, not defined val vpnConfigs = mutableListOf<VpnConfig>() val parcelables: Array<out Parcelable>? = appRestrictions.getParcelableArray("vpn_configuration_list") if (parcelables?.isNotEmpty() == true) { // iterate parcelables and cast as bundle parcelables.map { it as Bundle }.forEach { vpnConfigBundle -> // parse bundle data and store in VpnConfig array vpnConfigs.add(VpnConfig() .setServer(vpnConfigBundle.getString("vpn_server")) .setUsername(vpnConfigBundle.getString("vpn_username")) .setPassword(vpnConfigBundle.getString("vpn_password"))) } } if (vpnConfigs.isNotEmpty()) { // ...choose a VPN configuration or prompt user to select from list }
Java
// VpnConfig is a sample class used store config data, not defined List<VpnConfig> vpnConfigs = new ArrayList<>(); Parcelable[] parcelables = appRestrictions.getParcelableArray("vpn_configuration_list"); if (parcelables != null && parcelables.length > 0) { // iterate parcelables and cast as bundle for (int i = 0; i < parcelables.length; i++) { Bundle vpnConfigBundle = (Bundle) parcelables[i]; // parse bundle data and store in VpnConfig array vpnConfigs.add(new VpnConfig() .setServer(vpnConfigBundle.getString("vpn_server")) .setUsername(vpnConfigBundle.getString("vpn_username")) .setPassword(vpnConfigBundle.getString("vpn_password"))); } } if (!vpnConfigs.isEmpty()) { // ...choose a VPN configuration or prompt user to select from list }
监听受管配置变更
每当应用的受管配置发生更改时,系统都会触发 ACTION_APPLICATION_RESTRICTIONS_CHANGED
intent。您的应用必须监听此 intent,以便在配置设置发生变化时更改应用的行为。
注意:系统只会将 ACTION_APPLICATION_RESTRICTIONS_CHANGED
intent 发送给动态注册的监听器,而不会发送给应用清单中声明的监听器。
以下代码展示了如何为此 intent 动态注册广播接收器:
Kotlin
val restrictionsFilter = IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED) val restrictionsReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // Get the current configuration bundle val appRestrictions = myRestrictionsMgr.applicationRestrictions // Check current configuration settings, change your app's UI and // functionality as necessary. } } registerReceiver(restrictionsReceiver, restrictionsFilter)
Java
IntentFilter restrictionsFilter = new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED); BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // Get the current configuration bundle Bundle appRestrictions = myRestrictionsMgr.getApplicationRestrictions(); // Check current configuration settings, change your app's UI and // functionality as necessary. } }; registerReceiver(restrictionsReceiver, restrictionsFilter);
注意:通常,当应用处于暂停状态时,无需收到有关配置更改的通知。相反,您应在应用暂停时取消注册广播接收器。当应用恢复时,您首先检查当前的受管配置(如检查受管配置中所述),然后注册广播接收器,以确保在应用处于活动状态时收到有关配置变更的通知。
向 EMM 发送受管理的配置反馈
将受管理的配置更改应用于应用后,最佳做法是通知 EMM 更改的状态。Android 支持一项名为键控应用状态的功能,您可在应用每次尝试应用受管理配置更改时使用该功能发送反馈。此反馈可以作为确认您的应用成功设置了受管配置的依据;如果您的应用未能应用指定的更改,则其中可能会包含错误消息。
EMM 提供商能够检索此反馈并将其显示在其控制台中,以供 IT 管理员查看。如需详细了解此主题,包括有关如何为应用添加反馈支持的详细指南,请参阅向 EMM 发送应用反馈。
更多代码示例
ManagedConfigurations 示例进一步演示了如何使用本页介绍的 API。
在个人资料中将应用列入许可名单/屏蔽名单
第三方应用商店可能会表示有意使用受管理的配置,以便以可靠的方式将应用屏蔽名单或许可名单同时应用于个人资料和私密空间消费者功能,后者是供用户存放敏感应用的额外个人空间。如果您开发了面向企业使用的应用商店,并且希望使用此功能,请提交 此表单以表达您的意向,并在表单中选择对第三方应用商店许可名单感兴趣作为回复原因。