pAdTy_-3 使用系统权限

2015.11.01-11.02
个人英文阅读练习笔记(低水准)。原文地址:http://developer.android.com/training/permissions/index.html。

2015.11.01
为了顾及系统的健全和用户的隐私,安卓系统限制应用程序只可运行在/访问某段空间。如果应用程序想使用这段空间范围外的资源,应用程序需要显示的申请相应的权限。基于应用程序所申请的权限的类型,系统或许会自动授予相应的权限,或系统通知用户授予权限给应用程序。

此笔记记录怎么为应用程序声明和请求权限。

1. 声明权限

学习怎么在应用程序的清单文件中申请应用程序所需的权限。

安卓系统上的应用程序运行时具有有限的访问权限。如果应用程序想使用其权限之外的资源,应用程序必须申请访问相应资源的权限。通过在应用程序的 App Manifest中为所需的权限列表即可声明应用程序所需的权限。

基于所申请权限的敏感程度,系统会自动授权,或者系统叫设备用户来授权权限申请。例如,如果应用程序申请运行闪光灯的权限,系统自动授权此权限。如果应用程序要读用户的联系人时,系统会将权限给用户来决定是否让应用程序获得此权限。基于不同系统版本,用户授权权限在应用安装时(安卓5.1以及更低)或在应用运行时(安卓6.0或更高)。

(1) 决定应用程序所需的权限

在开发应用程序时,当应用程序使用获取权限的能力时需要格外留意。一般来说,当应用程序要使用的资源非本身所创造时就需要申请权限了,或者应用程序执行跟设备或者其它应用程序相关的操作时也需要申请权限。例如,访问互联网、使用设备的照相机,或者开或关WiFi,在应用程序中都应该申请相应的权限。关于系统权限,见 Normal and Dangerous Permissions。

应用程序申请权限只针对本应用程序会直接执行的操作。当应用程序从其它应用程序请求执行操作或获取信息时不需要申请权限。例如,如果应用程序要读通讯录时,应用程序需要获得READ_CONTACTS权限。如果应用程序通过目的从联系人应用程序获取联系人信息时,应用程序就不必申请权限,因为联系人应用程序不需要读通讯录的权限。更多信息见 Consider Using an Intent。

(2) 往清单文件中增加权限

要为应用程序申明权限,需要在应用程序的清单文件(app manifest)中的元素下添加一个元素。例如,若应用程序需要发送SMS信息则它需要在清单文件中包含以下内容:

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

    <uses-permission android:name="android.permission.SEND_SMS"/>


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

</manifest>

在申明权限后,系统的具体行为依赖权限的敏感程度。如果权限不涉及用户的隐私,系统会自动授权权限。如果权限关系到用户的隐私,系统会将授权的权限给用户来决定是否要授权给应用程序。更多关于不同权限的笔记请参见 Normal and Dangerous Permissions。

2. 在运行时申请权限

学习如何在应用程序运行时向用户申请权限。此笔记只适合安卓6.0(API 23)以及更高版本系统的设备。

从安卓6.0(API 23)开始,用户可以在应用程序运行时为用户授权权限申请(而不是安装应用程序时)。由于用户不用在应用程序安装或升级时为应用程序授权,这就简单化了应用程序的安装。同时,用户对应用程序功能有了更多的控制;例如,用户可以选择给照相机应用程序给访问相机的权限而不给此应用程序访问本设备坐标的权限。用户也可以在任何时候进入应用程序设置界面废除所给应用程序的权限。

系统权限被划分为两个类别,正常和危险:
- 正常的权限不会直接危及用户的隐私。如果应用程序的清单文件中声明了正常的权限,系统会自动授权这些权限的申请。
- 危险的权限将会给应用程序访问用户私密数据的权限。如果在应用程序的清单文件中声明了危险的权限,系统会将授权权限交给设备用户。

更多关于权限的笔记参见Normal and Dangerous Permissions。

在所有的安卓系统版本中,应用程序需要的正常或危险的权限都需要列在应用程序的清单文件中,此过程在 Declaring Permissions中描述。然而,在不同的系统版本和应用程序的目标SDK level下,权限声明的影响会不同:
- 如果设备跑的系统在安卓5.1或更低,应用程序的目标SDK为22或更低:若在清单文件中列举了危险权限,安卓应用程序时由用户授权权限申请;如果用户没有允许这些权限申请,系统不会安装此应用程序。
- 如果设备跑的安卓系统6.0或者更高版本,应用程序的目标SDK也在23或以上时:若应用程序在清单文件中声明了 权限,当应用程序运行时若有危险的权限申请则必须申请。用户可以对危险权限授权或拒绝,如果用户拒绝了应用程序的危险权限申请,应用程序也会按照无此危险权限的模式继续运行。

注:此笔记在API leverl 23或以上以及安卓系统6.0或以上的基础上记录如何实现应用程序中的权限。如果设备或者应用程序的targetSdkVersion为22或更低时,系统会让用户在应用程序安装时让用户授权。

此笔记描述如何用安卓的SupportLibrary库来检查、申请权限。6.0(API 23)以及以上的安卓框架提供了类似的方法。然而,使用支持库会更加简单,因为在调用方法时不必检查安卓设备跑的是什么版本的系统。

(1) 检查权限

当应用程序中需要危险权限时,在每次执行跟危险权限关联的操作时都要检查是否已经拥有了此危险权限的授权。用户可能会废除此权限,所以尽管应用程序昨天可以使用照相机,今天未必还有使用照相机的权限。

调用ContextCompat.checkSelfPermission()方法来检查应用程序是否拥有权限。例如,以下代码展示如何检查活动是否有写日历的权限:

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

如果用户拥有权限授权,此方法返回PackageManager.PERMISSION_GRANTED,应用程序就可以执行相应的操作。如果应用程序没有权限授权,此方法返回PERMISSION_DENIED,应用程序就需要显示的向用户申请权限授权。

(2) 请求权限

若应用程序需要被列在清单文件中的某危险权限,必须向用户申请此权限的授权。安卓提供了几种方法来申请权限授权。调用这些方法会弹出一个标准的安卓对话框,此对话框不能被自定义。

[1] 解释为什么应用程序需要权限
在某些情况下,您可能想帮助用户理解应用程序为什么需要权限。例如,当用户启动摄像应用程序时,用户可能不会惊讶摄像应用程序会请求访问照相机应用程序,但是用户可能不会理解为什么摄像应用程序会申请访问用户坐标或联系人的权限。在请求权限之前,您应该考虑给用户提供一个为什么需要此权限的解释。但需要记住的是,不要让解释烦到用户,如果不顾及用户感受就自动弹出太多解释,用户可能会觉得此应用程序有些令人沮丧并会卸载它。

当用户已经拒绝了应用程序的权限请求时是一个提供解释为什么需要此权限的机会。如果用户一直拒绝应用程序的权限申请,那就表示用户不知道此应用程序为何会请求此权限,此时提供一个解释是比较好的主意。

安卓提供了一个方法shouldShowRequestPermissionRationale()来帮助是否要给用户提供一个解释,此方法在应用程序请求权限并遭到用户拒绝时返回true。

注:当用户之前就在权限请求对话框中拒绝了应用程序的权限请求并选择了不要再请求的选择时,此方法返回false。当设备禁止应用程序的权限请求时,此方法也返回false。

[2] 请求所需的权限
当应用程序还未拥有所需权限的授权时,应该在应用程序中调用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()之前完成这些操作。见[1]。

[3] 操控权限请求的响应
当应用程序请求权限时,系统向用户呈现了一个对话框。当用户响应后,系统会将用户的响应传递给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
    }
}

系统将应用程序需要访问的权限以权限组(permission group)的方式显示在对话框中;对话框不会列举特定的权限。例如,如果请求READ_CONTACTS权限,对话框只是会提示应用程序需要访问设备的联系人。用户只能为每个权限组授权一次。如果应用程序在此组中(列在应用程序清单文件中)请求其它的权限,系统会自动授权。当请求权限授权时,系统会调用onRequestPermissionsResult() 回调方法并传递 PERMISSION_GRANTED给它,如果用户显示的授权后也会有相同的过程。

注:应用程序需要显示的请求每一个需要的权限,即使是用户已经在同一个组中为另外一个权限请求授了权。另外,权限分组可能会在将来的安卓版本中改变,所编写的代码要能够清楚的授权每一个权限。

例如,假设在清单文件中同时列举了 READ_CONTACTS和WRITE_CONTACTS,在请求 READ_CONTACTS时用户授权;然后再请求WRITE_CONTACTS时,系统会立即授权即不再经过用户授权。

当用户拒绝某权限授权时,应用程序要做合适的反应。例如,应用程序弹出一个为什么不能执行需要某授权(遭用户拒绝)的操作。

当系统给用户授权权限时,用户可以选择告知系统不再授权某权限。在这种情况下,若应用程序会再次用requestPermissions()申请,系统会立即拒绝此请权限求。系统会调用回调方法onRequestPermissionsResult()并将传递PERMISSION_DENIED给它,如果用户显示的拒绝了权限请求,会发生相同的过程。这意味着当调用时requestPermissions(),就不能假设有任何和用户发生过的交互。

2015.11.02

3. 权限最佳实践

学习请求并使用权限来创造最好的用户体验。

向用户请求权限这个操作很容易在应用程序中实现。但如果用户发现应用程序使用起来有沮丧感,或者用户怀疑应用程序使用此权限有不轨意图,那么用户会完全卸载此应用程序。接下来会介绍避免不好用户体验的最佳实践方式。

(1) 考虑用目的

在许多情况下,应用程序有两种方式来执行某项任务。应用程序可以通过请求权限的方式来执行某任务。可选的,应用程序也可以向其它应用程序发送目的来让其它应用程序代做某任务。

例如,假设应用程序需要用设备上的摄像头来拍照。应用可以申请CAMERA权限,这样应用程序就可以直接访问摄像头。应用程序调用摄像头的API就可以控制摄像头来拍照。这种方式给应用程序对摄像头完全的控制,并让您在应用程序中使用摄像头的UI。

然而,如果不需要绝对的控制摄像头,您可以使用 ACTION_IMAGE_CAPTURE目的去请求一张图片。当发送目的时,系统会提示用户选择一个摄像头应用程序(如果有默认的摄像头程序)。用户就可以通过所选择的摄像头应用程序拍照,并将所拍图片返回给您的应用程序的 onActivityResult() 方法。

类似的,像打电话访问用户联系人等,应用程序可以创建一个合适的目的或者请求权限直接访问对象。以下是两种方法各自的好与弊。

若用权限:
- 访问对象执行操作时应用程序能够直接控制对象来提供用户体验。然而,这会增加应用程序的复杂性,因为应用程序需要为此对象设计一个合适的用户界面。
- 只会给用户一次的授权提醒,在应用程序运行或安装时(基于用户使用的安卓系统的版本)。在用户授权后,应用程序直接操控对象不会再跟用户发生交互。然而,如果用户没有授权(或者废除了授权),应用程序就不能够操作对象了。

若用目的:
- 不必为此操作设计用户界面。接收目的的应用程序提供相关的用户界面。然而,这就意味着您不能控制用户体验。您不知道和用户交互的应用程序到底是什么样的。
- 如果用户没有能够执行此操作的应用程序,系统还是会提示用户选择一个应用程序。如果用户没有选择默认的应用程序来接收目的,每次运行到这个地方时都会提示用户选择一个应用程序。

(2) 只请求所需的权限

每当请求权限时,应用程序都会弹出对话框让用户作一个选择。应该最小化请求权限的次数。如果用户使用的安卓系统在6.0(API 23)以及以上,应用程序请求权限时会打断用户当前工作来让用户作选择。如果用户使用的更低的安卓系统,用户必须在应用程序安装时授权各种请求。如果请求权限的列表过长或似乎不太合适,用户根本不会安装你的应用程序。有了这些原因,您就应该尽量最小化应用程序的权限申请。

在很多时候可以使用目的来代替权限申请。如果此部分不是本应用程序的核心功能,那么您就可以考虑让另外一个应用程序来代工,参见 Consider Using An Intent.。

(3) 不要用频繁的权限请求来压倒用户

如果用户运行的安卓系统在6.0(API 23)或以上,在应用程序运行时用户会面临权限请求的选择。如果一下子就在用户面前呈现多个权限请求,这会让用户感觉压力的存在,这就可能会让用户不再想继续使用此应用程序。所以,只向用户请求当前所需的权限。

在某些情况下,有的权限可能是应用程序必须的。当应用程序启动时就需要这些权限。例如,摄像应用程序,它就必须访问摄像头。当用户第一次启动此应用时,用户不会惊讶应用程序请求使用摄像头的权限。但是,如果此摄像应用程序有分享用户联系人的功能,就不应该在第一次启动程序时就申请 READ_CONTACTS权限。当用户使用到分享功能时再请求此权限不迟。

如果应用程序提供了使用教程,在教程的末尾请求应用程序必须的权限是明智的选择。

(4) 解释为什么需要权限

当应用程序调用requestPermissions()来请求所需权限时系统会显示权限授权对话框给用户,此对话框上没有解释为何要请求此权限。在某些情况下,用户会为所请求权限感到迷惑。在调用requestPermissions()之前解释应用程序为什么需要此权限是个不错的主意。

例如,摄像应用程序可能需要设备地理位置信息来标记图片。有的用户可能不理解照片还需要包含其位置信息,那么他们就会对摄像机应用程序需要地理位置信息的权限而感到迷惑。像这样的情况,应用程序就可以解释为何需要地理位置的权限了。

通知用户权限请求的一种方式是将通知包含在应用程序的教程中。教程会一次展示应用程序的功能,如此,就可以跟随应用程序为什么需要某权限的原因。例如,摄像应用程序可以用“伴随联系人分享图片”,然后告诉用户此功能需要联系人权限。当然,不是所有的用户都会读教程,所以仍然需要在应用程序的权限请求中包含正常的提示与步骤。

(5) 测试两种权限模式

从安卓系统6.0(API 23)开始,用户授权或废除应用程序权限都是在应用程序运行时,而不是在应用程序安装时。这就需要在各种情况下测试应用程序请求权限功能是否正常。在安卓系统6.0以前,在应用程序运行时所有的权限都已经被授予了。

下面的提示将会帮助确认跟权限相关的问题(API lever 23及以上):
- 确定应用程序当前的权限和对应的代码路径。
- 测试用户授权与否时的服务和数据。
- 联合测试授权或者废除权限时的情况。例如,照相机应用程序可能会有CAMERA, READ_CONTACTS, and ACCESS_FINE_LOCATION 权限请求。您应该测试每个权限的授权和废除,以确保代码的正确性。
- 在命令行中用 adb 工具来管理权限
○ 以分组的方式列举权限和状态
adbshellpmlistpermissionsdg adb shell pm [grant|revoke] …
- 分析使用用户权限的服务。

[2015.11.11-10:07]

你可能感兴趣的:(android,系统权限)