andorid 运行时权限

从 Android 6.0(API 级别 23)开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授予。此方法可以简化应用安装过程,因为用户在安装或更新应用时不需要授予权限。它还让用户可以对应用的功能进行更多控制;例如,用户可以选择为相机应用提供相机访问权限,而不提供设备位置的访问权限。用户可以随时进入应用的“Settings”屏幕调用权限。

系统权限分为两类:正常权限危险权限

  • 正常权限不会直接给用户隐私权带来风险。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。
  • 危险权限会授予应用访问用户机密数据的权限。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。如果您列出了危险权限,则用户必须明确批准您的应用使用这些权限。

如需了解详细信息,请参阅正常权限和危险权限

在所有版本的 Android 中,您的应用都需要在其应用清单中同时声明它需要的正常权限和危险权限,如声明权限中所述。不过,该声明的影响因系统版本和应用的目标 SDK 级别的不同而有所差异:

  • 如果设备运行的是 Android 5.1 或更低版本,或者应用的目标 SDK 为 22 或更低:如果您在清单中列出了危险权限,则用户必须在安装应用时授予此权限;如果他们不授予此权限,系统根本不会安装应用。
  • 如果设备运行的是 Android 6.0 或更高版本,或者应用的目标 SDK 为 23 或更高:应用必须在清单中列出权限,并且它必须在运行时请求其需要的每项危险权限。用户可以授予或拒绝每项权限,且即使用户拒绝权限请求,应用仍可以继续运行有限的功能。

:从 Android 6.0(API 级别 23)开始,用户可以随时从任意应用调用权限,即使应用面向较低的 API 级别也可以调用。无论您的应用面向哪个 API 级别,您都应对应用进行测试,以验证它在缺少需要的权限时行为是否正常。

本课将介绍如何使用 Android 支持库来检查和请求权限。Android 框架从 Android 6.0(API 级别 23)开始提供类似方法。不过,使用支持库更简单,因为在调用方法前,您的应用不需要检查它在哪个版本的 Android 上运行。

检查权限


如果您的应用需要危险权限,则每次执行需要这一权限的操作时您都必须检查自己是否具有该权限。用户始终可以自由调用此权限,因此,即使应用昨天使用了相机,它不能假设自己今天仍具有该权限。

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

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);

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

请求权限


如果您的应用需要应用清单中列出的危险权限,那么,它必须要求用户授予该权限。Android 为您提供了多种权限请求方式。调用这些方法将显示一个标准的 Android 对话框,不过,您不能对它们进行自定义。

解释应用为什么需要权限

图 1. 提示用户授予或拒绝权限的系统对话框。

在某些情况下,您可能需要帮助用户了解您的应用为什么需要某项权限。例如,如果用户启动一个摄影应用,用户对应用要求使用相机的权限可能不会感到吃惊,但用户可能无法理解为什么此应用想要访问用户的位置或联系人。在请求权限之前,不妨为用户提供一个解释。请记住,您不需要通过解释来说服用户;如果您提供太多解释,用户可能发现应用令人失望并将其移除。

您可以采用的一个方法是仅在用户已拒绝某项权限请求时提供解释。如果用户继续尝试使用需要某项权限的功能,但继续拒绝权限请求,则可能表明用户不理解应用为什么需要此权限才能提供相关功能。对于这种情况,比较好的做法是显示解释。

为了帮助查找用户可能需要解释的情形,Android 提供了一个实用程序方法,即 shouldShowRequestPermissionRationale()。如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true

:如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don't ask again 选项,此方法将返回 false。如果设备规范禁止应用具有该权限,此方法也会返回 false

请求您需要的权限

如果应用尚无所需的权限,则应用必须调用一个 requestPermissions() 方法,以请求适当的权限。应用将传递其所需的权限,以及您指定用于识别此权限请求的整型请求代码。此方法异步运行:它会立即返回,并且在用户响应对话框之后,系统会使用结果调用应用的回调方法,将应用传递的相同请求代码传递到 requestPermissions()

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

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

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation 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,
                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.
    }
}

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

处理权限请求响应

当应用请求权限时,系统将向用户显示一个对话框。当用户响应时,系统将调用应用的 onRequestPermissionsResult() 方法,向其传递用户响应。您的应用必须替换该方法,以了解是否已获得相应权限。回调会将您传递的相同请求代码传递给 requestPermissions()。例如,如果应用请求 READ_CONTACTS 访问权限,则它可能采用以下回调方法:

@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_CONTACTS 和 WRITE_CONTACTS。如果您请求 READ_CONTACTS 且用户授予了此权限,那么,当您请求 WRITE_CONTACTS 时,系统将立即授予您该权限,不会与用户交互。

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

当系统要求用户授予权限时,用户可以选择指示系统不再要求提供该权限。这种情况下,无论应用在什么时候使用 requestPermissions() 再次要求该权限,系统都会立即拒绝此请求。系统会调用您的 onRequestPermissionsResult() 回调方法,并传递 PERMISSION_DENIED,如果用户再次明确拒绝了您的请求,系统将采用相同方式操作。这意味着当您调用 requestPermissions() 时,您不能假设已经发生与用户的任何直接交互。


权限使用说明

应用程序很容易通过权限请求来压倒用户。如果用户发现该应用令人沮丧,或者用户担心该应用可能对用户的信息做了什么,他们可能会避免使用该应用或完全卸载该应用。以下最佳实践可以帮助您避免这种不良的用户体验。

考虑使用意图


在许多情况下,您可以选择两种方式让您的应用程序执行任务。你可以让你的应用程序请求权限执行操作本身。或者,您可以让应用程序使用意图让另一个应用程序执行任务。

例如,假设您的应用程序需要能够使用设备相机拍照。您的应用可以请求CAMERA权限,这可以让您的应用直接访问摄像头。然后,您的应用程序将使用相机API来控制相机并拍摄照片。这种方法使您的应用程序完全控制摄影过程,并让您将摄像头用户界面整合到您的应用程序中。

但是,如果您不需要这样的完整控制,则可以使用ACTION_IMAGE_CAPTURE意图来请求图像。发送意图时,系统会提示用户选择相机应用程序(如果还没有默认的相机应用程序)。用户使用所选的相机应用拍摄照片,该应用将图片返回到您的应用的onActivityResult()方法。

同样,如果您需要拨打电话,访问用户的联系人等,可以通过创建适当的意图来实现,也可以直接请求权限并访问相应的对象。每种方法都有优点和缺点。

如果您使用权限:

  • 执行操作时,您的应用程序可完全控制用户体验。然而,这样广泛的控制会增加你的任务的复杂性,因为你需要设计一个合适的用户界面。
  • 系统会在运行时或安装时提示用户授予一次权限(具体取决于用户的Android版本)。之后,您的应用程序可以执行操作,而无需用户进行额外的交互。但是,如果用户没有授予权限(或稍后撤销),则您的应用程序根本无法执行操作。

如果您使用意图:

  • 您不必为操作设计UI。处理意图的应用程序提供了UI。但是,这意味着您无法控制用户体验。用户可以与您从未见过的应用进行互动。
  • 如果用户没有该操作的默认应用程序,则系统提示用户选择一个应用程序。如果用户没有指定默认处理程序,则每次执行操作时都可能需要经过一个额外的对话框。

只需要问你需要的权限


每当您要求获得许可时,您都会强制用户做出决定。您应该尽量减少您提出这些请求的次数。如果用户运行的是Android 6.0(API级别23)或更高版本,则每次用户尝试一些需要权限的新应用程序功能时,应用程序都必须通过权限请求中断用户的工作。如果用户正在运行Android的早期版本,则用户必须在安装应用程序时授予应用程序的每一个权限; 如果列表太长或看起来不合适,用户可能决定根本不安装您的应用程序。由于这些原因,您应该尽量减少您的应用程序需要的权限数量。

通常你的应用程序可以避免使用意图请求许可 如果某个功能不是应用程序功能的核心部分,则应考虑将该工作交给另一个应用程序,如考虑使用意图中所述

不要压倒用户


如果用户运行Android 6.0(API级别23)或更高版本,则用户必须在运行应用程序时授予您的应用程序的权限。如果你一次面对许多用户的权限请求,你可能会压倒用户,并导致他们退出你的应用程序。相反,你应该要求权限,因为你需要他们。

在某些情况下,一个或多个权限可能对您的应用程序绝对重要。应用程序启动后立即请求所有这些权限可能是有意义的。例如,如果您制作摄影应用程序,则应用程序需要访问设备摄像头。当用户第一次启动应用程序时,他们不会被要求使用相机的权限感到惊讶。但是,如果相同的应用程序也有一个功能,与用户共享的联系人的照片,你应该不会索要READ_CONTACTS首先启动权限。而是等到用户尝试使用“共享”功能,然后请求权限。

如果您的应用程序提供了一个教程,则在教程序列结尾处请求该应用程序的基本权限可能是有意义的。

解释为什么你需要权限


当您打电话时系统显示的权限对话框显示requestPermissions()您的应用程序需要 什么权限,但没有说明原因。在某些情况下,用户可能会发现困惑。向用户解释为什么你的应用程序在调用之前需要权限是一个好主意 requestPermissions()

例如,一个摄影应用可能想要使用位置服务,因此可以对照片进行地理标记。一个典型的用户可能不明白,一张照片可以包含位置信息,并会迷惑为什么他们的摄影应用程序想知道的位置。所以在这种情况下,应用程序调用 之前告诉用户这个功能是个好主意requestPermissions()

一种告知用户的方法是将这些请求合并到应用程序教程中。本教程可以依次展示每个应用程序的功能,并且可以解释需要什么权限。例如,摄影应用程序的教程可以演示其“与您的联系人共享照片”功能,然后告诉用户他们需要给应用程序权限以查看用户的联系人。应用程序可以打电话requestPermissions()询问用户的访问权限。当然,并不是每个用户都会遵循这个教程,所以在应用程序的正常运行过程中,您仍然需要检查和请求权限。

测试两种权限模型


从Android 6.0(API级别23)开始,用户在运行时授予和撤销应用程序权限,而不是在安装应用程序时这样做。因此,您必须在更广泛的条件下测试您的应用程序。在Android 6.0之前,您可以合理地假设,如果您的应用程序正在运行,则它拥有在应用程序清单中声明的​​所有权限。在新的权限模式下,您不能再做出这样的假设。

以下提示将帮助您识别运行API级别为23或更高的设备上与权限相关的代码问题:

  • 确定您的应用的当前权限和相关的代码路径。
  • 测试用户流经权限保护的服务和数据。
  • 测试与授予或撤销权限的各种组合。例如,相机应用可能会列出CAMERAREAD_CONTACTS以及 ACCESS_FINE_LOCATION 在其清单。您应该测试每个这些权限打开和关闭的应用程序,以确保应用程序可以处理所有权限配置优雅。请记住,从Android 6.0开始,用户可以为任何应用程序打开或关闭权限,甚至可以打开目标API级别为22或更低的应用程序。
  • 使用adb工具从命令行管理权限:
    • 按组列出权限和状态:
      $ adb shell pm列表权限-d -g
    • 授予或撤销一个或多个权限:
      $ adb shell pm [grant | revoke] <permission-name> ...

  • 分析您的应用程序中使用权限的服务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值