page.title=权限 page.tags=previewresources, androidm page.keywords=permissions, runtime, preview page.image={@docRoot}preview/features/images/permissions_check.png @jd:body

内容快览

本文内容

  1. 概览
  2. 为运行时权限编码
  3. 测试运行时权限
  4. 最佳做法

M 开发者预览版引入了一种新的应用权限模型,旨在简化用户安装和升级应用的过程。 如果在 M 预览版上运行的应用支持新权限模型,则用户无需在安装或升级应用时授予任何权限。相反,应用会根据需要请求权限,且系统将向用户显示一个请求权限的对话框。

如果应用支持新权限模型,则仍可在运行旧版 Android 的设备上使用旧权限模型安装并运行此应用。

概览

通过 M 开发者预览版,该平台引入了新的应用权限模型。 以下概述了此新模型的主要组件:

对于需要权限的功能,此权限模型将改变应用的行为方式。 以下概述了您调整此模型时所应遵循的开发实践:

注:如果应用主要面向 M 开发者预览版,则必须使用新权限模型。

截至 M 开发者预览版发布,并非所有 Google 应用均已完全实现新权限模型。 Google 会在 M 开发者预览版运行期间更新这些应用,以便严格遵守权限切换设置。

注:如果您的应用拥有自己的 API 接口,请先确保调用方具备访问该数据所需的必要权限,然后再代理权限。

系统应用和签名权限

通常,当用户安装应用时,系统仅授予应用 {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL}。但在某些情况下,系统将授予应用更多权限:

在这两种情况下,用户仍可随时撤销权限,具体方法是:转到系统的设置屏幕,然后选择应用 > 应用名称 > 权限。应用应在运行时继续检查权限,并根据需要请求权限。

前后兼容性

如果应用并非面向 M 开发者预览版,则即使是在 M 预览版设备上,该应用也会继续使用旧权限模型。 当用户安装应用时,系统将要求用户授予应用清单文件中列出的所有权限。

注:在运行 M 开发者预览版的设备上,用户可以从应用的“设置”屏幕禁用任何应用(包括旧版应用)的权限。 如果用户禁用某旧版应用的权限,则系统将以静默方式禁用相应的功能。 当应用尝试执行需要该权限的操作时,该操作不一定会导致出现异常。 相反,它可能会返回空数据集、报告错误或以其他方式表现出异常行为。 例如,如果您未经许可查询日历,则该方法会返回空数据集。

如果您在未运行 M 预览版的设备上使用新权限模型安装应用,则系统将采用与其他任何应用相同的方式处理:系统会在安装应用时要求用户授予声明的所有权限。

注:对于预览版本,您必须将最低 SDK 版本设置为 M 预览版 SDK,才可使用预览版 SDK 进行编译。 这意味着在开发者预览版运行期间,您无法在旧版平台上测试此类应用。

权限与意向的比较

许多情况下,您可以使用以下两种方式之一来让您的应用执行某项任务。 您可以将应用设置为请求执行操作本身所需的权限。 或者,您可以将应用设置为通过传送意向,让其他应用来执行任务。

例如,假设应用需要能够使用设备相机拍摄照片。 应用可以请求 android.permission.CAMERA 权限,以便允许其直接访问相机。 然后,应用将使用 Camera API 控制相机并拍摄照片。 利用此方法,您的应用能够完全控制摄影过程,并支持您将相机 UI 合并至应用中。

但是,如果您无需此类控制,则可仅使用 {@link android.provider.MediaStore#ACTION_IMAGE_CAPTURE ACTION_IMAGE_CAPTURE} 意向来请求图像。 启动该意向时,系统会提示用户选择相机应用(如果没有默认相机应用),然后该应用将拍摄照片。 该相机应用会将照片返回给应用的 {@link android.app.Activity#onActivityResult onActivityResult()} 方法。

同样,如果您需要打电话、访问用户的联系人或要执行其他操作,则可通过创建适当的意向来完成,或者您可以请求相应的权限并直接访问相应的对象。 每种方法各有优缺点。

如果使用权限:

如果使用意向:

为运行时权限编码

如果应用主要面向新的 M 开发者预览版,则您必须使用新权限模型。 这意味着除了通过清单文件声明所需的权限以外,您还必须检查应用运行时是否已有相应的权限,如果没有,则需要请求权限。

启用新权限模型

要启用新的 M 开发者预览版权限模型,请将应用的 targetSdkVersion 属性设置为 "MNC",并将 compileSdkVersion 设置为 "android-MNC"。这样可启用所有新的权限功能。

对于预览版本,您必须将 minSdkVersion 设置为 "MNC",才能使用预览版 SDK 进行编译。

指定仅用于 M 预览版的权限

您可以使用应用清单文件中的新 <uses-permission-sdk-m> 元素指明仅在 M 开发者预览版中需要某权限。 如果您以这种方式声明权限,则每当在旧版设备上安装应用时,系统都不会提示用户或向应用授予权限。通过使用 <uses-permission-sdk-m> 元素,您可以将新权限添加到更新后的应用版本,而不必强制用户在安装更新时授予权限。

如果应用在已安装 M 开发者预览版的设备上运行,则 <uses-permission-sdk-m> 的行为与 <uses-permission> 相同。 系统不会在安装应用时提示用户授予任何权限,且应用将根据需要请求权限。

提示授予权限

如果应用使用新的 M 开发者预览版权限模型,则在运行 M 预览版的设备上首次启动应用时,系统不会要求用户授予所有权限。 相反,应用将根据需要请求权限。 当应用请求某权限时,系统会向用户显示一个对话框。

如果应用在已安装 SDK 22 或更低版本的设备上运行,则应用将使用旧权限模型。 当用户安装应用时,系统将提示他们授予应用在清单文件中请求的所有权限,但那些带有 <uses-permission-sdk-m> 标记的权限除外。

检查运行应用的平台

只有运行 M 开发者预览版的设备支持此权限模型。 在调用其中任何方法之前,应用均应通过检查 {@link android.os.Build.VERSION#CODENAME Build.VERSION.CODENAME} 的值来验证运行应用的平台。 如果设备正在运行 M 开发者预览版,则 {@link android.os.Build.VERSION#CODENAME CODENAME} 为 "MNC"

检查应用是否具备所需的权限

当用户尝试执行需要权限的操作时,应用将检查目前是否具备执行此操作所需的权限。 为此,应用将调用 Context.checkSelfPermission( permission_name)。即便知道用户已授予该权限,应用也应执行此检查,因为用户可以随时撤销应用的权限。 例如,如果用户需要使用应用拍摄照片,则应用将调用 Context.checkSelfPermission(Manifest.permission.CAMERA)

表 1.权限和权限组。

权限组 权限
android.permission-group.CALENDAR
  • android.permission.READ_CALENDAR
  • android.permission.WRITE_CALENDAR
android.permission-group.CAMERA
  • android.permission.CAMERA
android.permission-group.CONTACTS
  • android.permission.READ_CONTACTS
  • android.permission.WRITE_CONTACTS
  • android.permission.READ_PROFILE
  • android.permission.WRITE_PROFILE
android.permission-group.LOCATION
  • android.permission.ACCESS_FINE_LOCATION
  • android.permission.ACCESS_COARSE_LOCATION
android.permission-group.MICROPHONE
  • android.permission.RECORD_AUDIO
android.permission-group.PHONE
  • android.permission.READ_PHONE_STATE
  • android.permission.CALL_PHONE
  • android.permission.READ_CALL_LOG
  • android.permission.WRITE_CALL_LOG
  • com.android.voicemail.permission.ADD_VOICEMAIL
  • android.permission.USE_SIP
  • android.permission.PROCESS_OUTGOING_CALLS
android.permission-group.SENSORS
  • android.permission.BODY_SENSORS
  • android.permission.USE_FINGERPRINT
android.permission-group.SMS
  • android.permission.SEND_SMS
  • android.permission.RECEIVE_SMS
  • android.permission.READ_SMS
  • android.permission.RECEIVE_WAP_PUSH
  • android.permission.RECEIVE_MMS
  • android.permission.READ_CELL_BROADCASTS

根据需要请求权限

如果应用尚无所需的权限,则应用将调用 Activity.requestPermissions(String[], int) 方法,请求提供一项或多项适当的权限。 应用将传递所需的一项或多项权限,以及整数“请求代码”。 此方法异步运行:它会立即返回,并且在用户响应对话框之后,系统会使用结果调用应用的回调方法,将应用传递的相同“请求代码”传递到 requestPermissions()

以下代码检查应用是否具备读取用户联系人所需的权限,并根据需要请求该权限:

if (checkSelfPermission(Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {
    requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
            MY_PERMISSIONS_REQUEST_READ_CONTACTS);

    // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
    // app-defined int constant

    return;
}

处理权限请求响应

当应用请求权限时,系统将向用户显示一个对话框。 当用户响应时,系统将调用应用的 Activity.onRequestPermissionsResult(int, String[], int[]) ,向其传递用户响应。应用需要替代该方法。回调会将您传递的相同请求代码传递给 requestPermissions()。 例如,如果应用请求 READ_CONTACTS 访问权限,则可能采用以下回调方法:

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! do the
                // calendar task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'switch' lines to check for other
        // permissions this app might request
    }
}

如果用户授予权限,则系统会为应用授予应用清单文件为该功能区域列出的所有权限。 如果用户拒绝请求,则您应采取适当的操作。 例如,您可以禁用任何取决于此权限的菜单操作。

当系统要求用户授予权限时,用户可以选择指示系统不再要求提供该权限。 在这种情况下,当应用使用 requestPermissions() 请求该权限时,系统会立即拒绝此请求。 在这种情况下,如果用户已再次明确拒绝您的请求,则系统会以同样的方式调用您的 onRequestPermissionsResult()。 因此,您的应用不能假设用户采取了任何直接交互行为。

测试运行时权限

如果应用主要面向 M 开发者预览版,则您必须测试它是否正确处理权限。 您不能假设应用在运行时具备任何特定的权限。 应用首次启动时,它可能没有任何权限,且用户可以随时撤销或恢复权限。

您应测试应用,确保它在所有权限情况下均可正常运行。 通过 M 预览版 SDK,我们提供了新的 Android Debug Bridge (adb) 命令,支持您使用需要尝试的任何权限设置测试应用。

新 adb 命令和选项

M 预览版 SDK 平台工具提供了多个新命令,支持您测试应用处理权限的方式。

使用权限安装

您可以使用 adb install 命令的新 -g 选项,该选项将安装应用并授予其清单文件中列出的所有权限:

$ adb install -g <path_to_apk>

授予和撤销权限

您可以使用新的 ADB 软件包管理器 (pm) 命令向已安装的应用授予权限和撤销其权限。此功能对于自动化测试非常有用。

要授予权限,请使用软件包管理器的 grant 命令:

$ adb pm grant <package_name> <permission_name>

例如,要向录音​​授予 com.example.myapp 软件包权限,请使用以下命令:

$ adb pm grant com.example.myapp android.permission.RECORD_AUDIO

要撤销权限,请使用软件包管理器的 revoke 命令:

$ adb pm revoke <package_name> <permission_name>

最佳做法

新权限模型为用户带来更流畅的体验,让他们能够更轻松地安装应用,并得心应手地使用应用的各项功能。 为了充分利用该新模型,我们建议采用下列最佳做法。

仅请求自己所需的权限

每次您请求权限时,实际上是在强迫用户作出决定。 如果用户拒绝请求,则会减少应用的功能。 您应尽量减少提出这些请求的次数。

例如,应用往往可以通过使用 意向(而不是请求权限)获得所需的功能。 如果应用需要使用手机的相机拍摄照片,则可使用 {@link android.provider.MediaStore#ACTION_IMAGE_CAPTURE MediaStore.ACTION_IMAGE_CAPTURE} 意向。当应用执行该意向时,系统会提示用户选择已安装的相机应用拍摄照片。

不要让用户感到无所适从

如果您让用户一次面对大量权限请求,用户可能会感到无所适从并因此退出应用。替代做法是,您应根据需要请求权限。

某些情况下,您的应用可能绝对需要一项或多项权限。在这种情况下,合理的做法是,在应用启动之后立即请求所有权限。 例如,如果您运行摄影应用,则该应用需要访问设备的相机。 当用户首次启动该应用时,不会对请求使用相机所需的权限感到惊讶。 但是,如果同一应用还具备与用户联系人共享照片的功能,则您不应在首次启动时请求用户提供该权限, 而是等到用户尝试使用“共享”功能之后,再请求该权限。

如果应用提供了教程,则合理的做法是,在教程结束时请求提供应用的必要权限。

解释需要权限的原因

系统在您调用 requestPermissions() 时显示的权限对话框将说明应用所需的权限,但不会解释为何需要这些权限。 在某些情况下,用户可能会感到困惑。 最好在调用 requestPermissions() 之前向用户解释应用需要权限的原因。

例如,摄影应用可能需要使用位置服务,以便能够为照片添加地理标签。 通常,用户可能不了解照片能够包含位置信息,并且对摄影应用想要了解具体位置感到不解。 因此在这种情况下,应用最好在调用 requestPermissions() 之前告知此功能的相关信息。

其中一种办法是将这些请求纳入应用教程。这样,教程可以依次显示应用的每项功能,并在显示每项功能时解释需要哪些相应的权限。 例如,摄影应用的教程可以演示其“与您的联系人共享照片”功能,然后告知用户需要为应用授予权限以便其查看用户的联系人。 然后,应用可以调用 requestPermissions(),要求用户提供该访问权限。 当然,并非所有用户都会按照教程操作,因此您仍需在应用的正常操作期间检查和请求权限。