The Android Developer Challenge is back! Submit your idea before December 2.

请求应用权限

每款 Android 应用都在访问受限的沙盒中运行。如果应用需要使用其自己的沙盒外的资源或信息,则必须请求相应权限。 要声明您的应用需要某项权限,您可以在应用清单中列出该权限,然后在运行时请求用户批准每项权限(适用于 Android 6.0 及更高版本)。

本页介绍如何使用 Android 支持库来检查和请求权限。Android 框架从 Android 6.0(API 级别 23)开始提供类似的方法,但如果使用支持库,将会更容易与较低的 Android 版本实现兼容性。

向清单添加权限

对于所有 Android 版本,要声明应用需要某项权限,请在应用清单中添加 <uses-permission> 元素,作为顶级 <manifest> 元素的子项。例如,需要访问互联网的应用需在清单中添加以下代码行:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.example.snazzyapp">

        <uses-permission android:name="android.permission.INTERNET"/>
        <!-- other permissions go here -->

        <application ...>
            ...
        </application>
    </manifest>
    

系统在您声明权限之后的行为取决于权限的敏感程度。有些权限被视为“常规”权限,因此系统会在安装应用时立即授予这些权限。还有些则被视为“危险”权限,因此需要用户明确向您的应用授予相应访问权限。如需详细了解不同类型的权限,请参阅保护级别

检查权限

如果应用需要一项危险权限,那么每次执行需要该权限的操作时,您都必须检查自己是否具有该权限。从 Android 6.0(API 级别 23)开始,用户可随时从任何应用撤消权限,即使应用以较低的 API 级别为目标平台也是如此。因此,即使应用昨天使用了相机,也不能认为它今天仍具有该权限。

要检查您是否具有某项权限,请调用 ContextCompat.checkSelfPermission() 方法。例如,以下代码段展示了如何检查 Activity 是否具有向日历写入数据的权限:

Kotlin

    if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR)
            != PackageManager.PERMISSION_GRANTED) {
        // Permission is not granted
    }
    

Java

    if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR)
            != PackageManager.PERMISSION_GRANTED) {
        // Permission is not granted
    }
    

如果应用具有此权限,该方法将返回 PERMISSION_GRANTED,并且应用可以继续操作。如果应用不具备此权限,该方法将返回 PERMISSION_DENIED,且应用必须明确要求用户授予权限。

请求权限

当您的应用从 checkSelfPermission() 收到 PERMISSION_DENIED 时,您需要提示用户授予该权限。Android 为您提供了几种可用来请求权限的方法(如 requestPermissions()),如下面的代码段所示。调用这些方法时,会显示一个无法自定义的标准 Android 对话框。

如何向用户显示此内容取决于设备的 Android 版本以及应用的目标版本,如权限概览中所述。

解释为什么应用需要权限

在某些情况下,您需要帮助用户理解为什么您的应用需要某项权限。例如,如果用户启动一款摄影应用,用户或许不会对该应用请求使用相机的权限感到惊讶,但用户可能不理解为什么该应用想要访问用户的位置或联系人。在您的应用请求权限之前,不妨考虑向用户提供解释。请注意,您肯定不希望您的解释让用户不胜其烦;如果您提供太多解释,用户可能会觉得这款应用很麻烦,因而将其移除。

您可以使用的一种方法是只在用户之前拒绝过该权限请求的情况下才提供解释。Android 提供了一种实用程序方法,即 shouldShowRequestPermissionRationale()。如果用户之前拒绝了该请求,该方法将返回 true;如果用户之前拒绝了某项权限并且选中了权限请求对话框中的不再询问选项,或者如果设备政策禁止该权限,该方法将返回 false

如果用户不断尝试使用需要某项权限的功能,但一直拒绝权限请求,这或许意味着,用户不理解为什么应用需要该权限才能提供这项功能。在这种情况下,显示解释或许是不错的做法。

应用权限最佳做法中提供了更多建议,指导您如何在请求权限时创造良好的用户体验。

请求您需要的权限

如果您的应用还不具备它需要的权限,那么它必须调用一个 requestPermissions() 方法来请求相应权限。您的应用将传递它想要的权限,以及您指定用于识别此权限请求的整数请求代码。此方法异步运行。它会立即返回结果,并且在用户响应提示后,系统会利用相应结果来调用应用的回调方法,在调用过程中传递的请求代码与应用传递给 requestPermissions() 的请求代码相同。

以下代码将检查应用是否具有读取用户联系人的权限。如果没有该权限,它会检查是否应显示需要该权限的解释,如果不需要解释,它会请求该权限:

Kotlin

    // Here, thisActivity is the current activity
    if (ContextCompat.checkSelfPermission(thisActivity,
            Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {

        // Permission is not granted
        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_CONTACTS)) {
            // Show an explanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.
        } else {
            // No explanation needed, we can request the permission.
            ActivityCompat.requestPermissions(thisActivity,
                    arrayOf(Manifest.permission.READ_CONTACTS),
                    MY_PERMISSIONS_REQUEST_READ_CONTACTS)

            // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
            // app-defined int constant. The callback method gets the
            // result of the request.
        }
    } else {
        // Permission has already been granted
    }
    

Java

    // Here, thisActivity is the current activity
    if (ContextCompat.checkSelfPermission(thisActivity,
            Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {

        // Permission is not granted
        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_CONTACTS)) {
            // Show an explanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.
        } else {
            // No explanation needed; request the permission
            ActivityCompat.requestPermissions(thisActivity,
                    new String[]{Manifest.permission.READ_CONTACTS},
                    MY_PERMISSIONS_REQUEST_READ_CONTACTS);

            // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
            // app-defined int constant. The callback method gets the
            // result of the request.
        }
    } else {
        // Permission has already been granted
    }
    

系统显示的提示说明了您的应用需要访问的权限组,而不是具体权限。

注意:当您的应用调用 requestPermissions() 时,系统会向用户显示一个标准对话框。您的应用无法配置或更改该对话框。如果您需要向用户提供任何信息或解释,您应该在调用 requestPermissions() 之前提供,如解释为什么应用需要权限中所述。

处理权限请求响应

当用户响应您应用的权限请求时,系统会调用您应用的 onRequestPermissionsResult() 方法,在调用过程中向其传递用户响应。您的应用必须替换该方法,以查明是否已向其授予相应权限。在回调过程中传递的请求代码与传递给 requestPermissions() 的请求代码相同。例如,如果应用请求 READ_CONTACTS 访问权限,则它可能采用以下回调方法:

Kotlin

    override fun onRequestPermissionsResult(requestCode: Int,
            permissions: Array<String>, grantResults: IntArray) {
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_READ_CONTACTS -> {
                // If request is cancelled, the result arrays are empty.
                if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return
            }

            // Add other 'when' lines to check for other
            // permissions this app might request.
            else -> {
                // Ignore all other requests.
            }
        }
    }
    

Java

    @Override
    public void onRequestPermissionsResult(int requestCode,
            String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return;
            }

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

系统显示的对话框说明了您的应用需要访问的权限组,它不会列出具体权限。例如,如果请求 READ_CONTACTS 权限,系统对话框只会说明您的应用需要访问设备的联系人。用户只需要针对每个权限组授予一次权限。如果您的应用请求该组中的其他任何权限(已在您的应用清单中列出),系统会自动授予这些权限。当您请求权限时,系统会调用您的 onRequestPermissionsResult() 回调方法并传递 PERMISSION_GRANTED,就像用户通过系统对话框明确同意了您的请求时的处理方式一样。

注意:您的应用仍需要明确请求它需要的每项权限,即使用户已授予同一组中的其他权限。此外,权限分组在将来的 Android 版本中可能会发生变化。您的代码不应依赖于特定权限属于或不属于同一组的假设。

例如,假设您在应用清单中同时列出了 READ_CONTACTSWRITE_CONTACTS。如果您请求 READ_CONTACTS 且用户授予了该权限,那么当您随后又请求 WRITE_CONTACTS 时,系统会立即授予您该权限,而无需与用户交互。

如果用户拒绝了权限请求,您的应用应采取适当的措施。例如,您的应用可能会显示一个对话框,解释它为什么无法执行用户请求的需要该权限的操作。

当系统要求用户授予某项权限时,用户可以选择告知系统不要再次请求该权限。在这种情况下,只要应用使用 requestPermissions() 再次请求该权限,系统就会立即拒绝请求。系统会调用您的 onRequestPermissionsResult() 回调方法并传递 PERMISSION_DENIED,就像用户再次明确拒绝了您的请求时的处理方式一样。如果设备政策禁止应用具有该权限,该方法也会返回 false。这意味着,当您调用 requestPermissions() 时,您不能假定已经发生与用户的任何直接交互。

要在请求应用权限时提供最佳用户体验,另请参阅应用权限最佳做法

按 API 级别声明权限

要仅在支持运行时权限的设备(即运行 Android 6.0(API 级别 23)或更高版本的设备)上声明某项权限,请添加 uses- permission-sdk-23 标记,而不是 uses-permission 标记。

使用这些标记中的任意一个时,您均可设置 maxSdkVersion 属性,以指定在运行更高版本的设备上不需要特定权限。

其他资源

如需详细了解权限,请阅读以下文章:

如需详细了解如何请求权限,请下载以下示例应用: