序言

实现 app 的限制

编写:zenlynn 原文:http://developer.android.com/training/enterprise/app-restrictions.html

如果你为企业市场开发 app ,你可能需要满足企业政策的特殊要求。应用程序的限制允许企业管理员远程设定 app 。这种能力对于部署了 managed profile 的企业 app 来说,尤其有用。

例如,一个企业可能需要核准的 app 允许企业管理员:

  • 为一个网页浏览器添加白名单或黑名单网址
  • 配置是否允许一个 app 通过蜂窝网络同步内容,或只能通过 Wi-Fi
  • 配置 app 的电子邮件设定

这个指南展示了如何在你的 app 实现这个配置设定。

注意:由于历史原因,这些配置设定被称为限制,并在文件与类中使用这个术语(例如 RestrictionsManager)。然而,这些限制实际上可以实现各种各样的配置选项,并不只是限制 app 的功能。

远程配置概述

app 定义了管理员可以远程设定的限制和配置选项。限制提供者可以随意改变配置设定。如果你的 app 运行在企业设备上的 managed profile 中 ,企业管理员可以改变该 app 的限制。

限制提供者是运行在同一个设备上的另一个 app 。这个 app 通常是由企业管理员控制。企业管理员向限制提供者 app 传达限制的改变。这个 app 就相应地改变你的 app 的限制。

提供外部可配置的限制:

  • 在你 app 的 manifest 中声明限制。这么做允许企业管理员通过 Goodle Play 的接口读取 app 的限制。
  • 每当 app 恢复,使用 RestrictionsManager 对象检查当前限制,并改变你的 app 的 UI 和行为以符合这些限制。

定义 app 的限制

你的 app 支持任何你想要定义的限制。你在限制文件中声明 app 的限制,在 manifest 中声明限制文件。创建一个限制文件允许其他 app 检查你的 app 提供的限制。企业移动管理(EMM)合作者可以通过 Google Play 接口来读取你的 app 的限制。

为了定义你的 app 的远程配置选项,把以下元素放在你的 manifest 中的 \ 元素里。

<meta-data android:name="android.content.APP_RESTRICTIONS"
    android:resource="@xml/app_restrictions" />

res/xml 文件夹中创建一个名为 app_restrictions.xml 的文件。该文件的结构在 RestrictionsManager的参考文献中有所 描述。该文件有一个单独的顶级的 <restrictions> 元素,这个元素包括一个 <restriction> 子元素对应 app 的每一个配置选项。

注意:不要创建限制文件的地区化版本。你的 app 只允许有一个限制文件,这样你的 app 在所有地区的限制才会保持一致。

在一个企业环境中,EMM 一般会使用该限制的框架为 IT 管理员生成远程控制台,所以管理员可以远程配置你的 app 。

例如,假设你的 app 可以被远程配置允许或禁止它在蜂窝连接下下载数据。你的 app 就会有一个像这样的 <restriction> 元素:

<?xml version="1.0" encoding="utf-8"?>
<restrictions xmlns:android="http://schemas.android.com/apk/res/android" >

  <restriction
    android:key="download_on_cell"
    android:title="@string/download_on_cell_title"
    android:restrictionType="bool"
    android:description="@string/download_on_cell_description"
    android:defaultValue="true" />

</restrictions>

RestrictionsManager 的参考文献中记载了 android:restrictionType 元素所支持的类型。

注意:Goole Play for Work 不支持 bundlebundle_array 限制类型。

使用每个限制的 android:key 属性从限制 bundle 中读取它的值。为此,每个限制必须有一个独特的 key 字符串,并且不能被地区化。它必须用一个 string 直接量指明。

注意:如在资源地区化所说,在一个产品 app 中,android:titleandroid:description 应该从地区化资源文件中提取出来。

限制提供者可以询问 app 来找到该 app 可用限制的细节,包括它们的描述文本。限制提供者和企业管理员可以在任何时候,甚至 app 没有在运行的时候,改变它的限制。

检查 app 的限制

当其他 app 改变你的 app 的限制设定时,你的 app 不会被自动通知。反而需要你在 app 启动或恢复的时候检查有哪些限制,并且监听系统 intent 来发现当你的 app 运行的时候限制是否发生改变。

为了知道当前限制设定,你的 app 使用一个 RestrictionsManager 对象。你的 app 应该在以下时候检查当前限制:

  • 当 app 启动或者恢复的时候,在它的 onResume() 方法里检查

为了获得一个 RestrictionsManager 对象,使用 getActivity() 取得当前 activity,然后调用 activity 的 Activity.getSystemService() 方法:

RestrictionsManager myRestrictionsMgr =
    (RestrictionsManager) getActivity()
        .getSystemService(Context.RESTRICTIONS_SERVICE);

一旦你有了 RestrictionsManager,你可以通过调用它的 getApplicationRestrictions() 方法取得当前的限制设定:

Bundle appRestrictions = myRestrictionsMgr.getApplicationRestrictions();

注意:方便起见,你也可以用 UserManager 取得当前限制,调用 UserManager.getApplicationRestrictions() 即可。这个方法与 RestrictionsManager.getApplicationRestrictions() 起到完全相同的作用。

getApplicationRestrictions() 方法需要从数据存储区获得数据,所以要尽量少用。不要每次你需要知道当前限制的时候就调用这个方法。你应该只在你的 app 启动或恢复的时候调用,并且缓存所取得的限制 bundle。然后,如监听 app 配置的改变中所说,在你的 app 活动的时候,监听 ACTION_APPLICATION_RESTRICTIONS_CHANGED intent 来发现限制是否改变。

当你的 app 使用 RestrictionsManager.getApplicationRestrictions() 检查限制时,我们建议你检查企业管理员是否把键值对 KEY_RESTRICTIONS_PENDING 设置为 true。如果设置了,你应该阻止用户使用这个 app,并提示他们联系他们的企业管理员。然后,这个 app 应该继续正常运行,注册 ACTION_APPLICATION_RESTRICTIONS_CHANGED 广播。

Figure 1. 在注册广播之前检查限制是否暂挂

读取并应用限制

getApplicationRestrictions() 方法返回一个 Bundle,其中包含了被设置的每个限制的键值对。这些值的类型是 Boolean, int, String, String[], Bundle, Bundle[]。只要你有了限制 Bundle ,你就可以用标准的 Bundle 方法针对数据类型来检查当前的限制设置,比如 getBoolean() 或者 getString()

注意:限制 Bundle 为每个被限制提供者显式设置的限制都包括了一个条目。但是,你不能只因为你在限制 XML 文件中定义了一个默认值,就假定这个限制就会在 bundle 里出现。

你基于当前的限制设定,为你的 app 采取合适的行动。比如,如果你的 app 有一个限制架构来指明是否它能在蜂窝连接(就像在定义 app的 限制的例子里一样)中下载,而你发现限制设置为 false,那么你不得不禁止数据下载,除非设备在 Wi-Fi 连接下,正如下面的实例代码所展示的:

boolean appCanUseCellular;

if appRestrictions.containsKey("downloadOnCellular") {
    appCanUseCellular = appRestrictions.getBoolean("downloadOnCellular");
} else {
    // here, cellularDefault is a boolean set with the restriction's
    // default value
    appCanUseCellular = cellularDefault;
}

if (!appCanUseCellular) {
    // ...turn off app's cellular-download functionality
    // ...show appropriate notices to user
}

注意:该限制架构必须向前向后兼容,因为 Google Play for Work 对于每个 app 只给予 EMM 一个版本的限制架构。

监听 app 限制的改变

每当 app 的限制被改变,系统就创建 ACTION_APPLICATION_RESTRICTIONS_CHANGED intent。你的 app 必须监听这个 intent,这样你就能在限制设定改变的时候改变 app 的行为。

注意:ACTION_APPLICATION_RESTRICTIONS_CHANGED intent 只发送给动态注册的监听者,而不发送给在 app manifest 里声明的监听者。

以下代码展示了如何为这个 intent 动态注册一个广播接收者:

IntentFilter restrictionsFilter =
    new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);

BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() {
  @Override public void onReceive(Context context, Intent intent) {

    // Get the current restrictions bundle
    Bundle appRestrictions =

    myRestrictionsMgr.getApplicationRestrictions();

    // Check current restrictions settings, change your app's UI and
    // functionality as necessary.

  }

};

registerReceiver(restrictionsReceiver, restrictionsFilter);

注意:一般来说,当你的 app 中止时不需要被通知限制的改变。相反,这个时候你需要注销你的广播接收者。当 app 恢复时,你首先要检查当前的限制(正如在检查 app 的限制中所讨论的),然后注册你的广播接收者,以保证在 app 活动期间如果有限制改变你会被通知。