Working with System Permissions

本文参考:https://developer.android.com/training/permissions/index.htmlhttps://www.youtube.com/watch?v=C8lUdPVSzDk&list=PLWz5rJ2EKKc-lJo_RGGXL2Psr8vVCTWjM&index=29#t=263.526485

为了保护系统的完整性和用户的隐私,Android在一个访问限制的sandbox中运行每个App。如果App想使用自身sandbox之外的资源或者信息,必须显示地请求权限。根据权限的类型,系统会自动授权,或者询问用户去授权。
这篇文章向你展示如何为你的App声明和请求权限。


一、Declaring Permissions

参考:https://developer.android.com/training/permissions/declaring.html

每个Android App运行在一个访问有限的sandbox。如果一个App需要sandbox之外的资源和信息,此App必须请求相应的权限。你可以在App Manifest中声明你需要的权限。

根据权限的敏感度,系统会自动授权或者需要用户授权。比如,如果你的App请求开启闪光灯的权限,系统自动授权。但是,如果你的App需要访问联系人,系统会询问用户去授权。根据Android的版本不同,用户可以在安装App时授权(Android 5.1及之前),或者运行App时授权(Android 6.0及之后)。

1.1、Determine What Permissions Your App Needs

当你开发App时,需要注意你的App使用了哪些需要权限的功能。典型的,当一个App使用非自身创建的信息或资源时,或者执行影响设备或其它App行为的操作时,都需要权限。比如,如果一个App需要访问互联网、使用相机、开启/关闭Wi-Fi时,都需要合适的权限。
想要查看 normal 和 dangerous权限,请阅读“三、Normal and Dangerous Permissions”。

你的App只有在直接执行某些操作时才需要权限,当它请求其它App执行任务或提供信息时不需要权限。比如,你的App需要读取用户的联系人,需要 READ_CONTACTS 权限;但是,如果你的App使用一个 intent 从 Contact App 中请求数据时,你的App不需要任何权限,但是 Contact App 必须拥有
READ_CONTACTS 权限。

1.2、Add Permissions to the Manifest

为了声明App需要的权限,在manifest中使用 元素,作为 元素的子元素。比如,需要发送短信的Appmanifest需要声明如下权限:

<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 权限,系统会自动授权;如果是访问用户隐私信息的 dangerous 权限,系统会询问用户去同意。请阅读“三、Normal and Dangerous Permissions”去了解 normal和dangerous 权限的区别。


二、Requesting Permissions at Run Time

参考:https://developer.android.com/training/permissions/requesting.html

从Android 6.0(API 23)开始,用户授权权限发生在App运行时,而不是安装App时。这使得安装App的过程更加流畅,因为用户在安装或升级App时不需要授权权限。这也让用户对于App的功能具有更多的控制权,比如,一个用户会授权一个Camera App使用相机,但是不允许获取位置信息。而且,用户可以在任何时候进入系统的应用设置界面,然和取消对这些权限的授权。

系统权限分为两大类:normal和dangerous。
normal权限不会直接对用户的隐私造成风险。你的App在manifest中列出一些normal权限,系统会自动同意这些权限,并且用户也无法取消这些权限。
dangerous权限能够让App访问用户的隐私数据。不像normal权限,如果你的App在manifest中列了一些dangerous权限,用户必须显示地同意这些权限,也就是运行时手动授权或者在系统设置界面进行授权。
想了解更多信息,请阅读“三、Normal and Dangerous Permissions”。

不论什么Android版本,你的App都需要将所需的normal和dangerous权限声明在manifest中。然而,Android系统的版本和App的target sdks level会使声明权限的影响有些区别。
如果设备运行在Android 5.1及以下,或者你的App target SDK是22及以下:如果你在manifest中列出了dangerous权限,用户安装时必须同意这些权限,否则系统不会安装App。
如果设备运行在Android 6.0及以上,并且你的App targer SDK是23及以上:你的App需要在manifest中列出所需的normal 和 dangerous权限,系统会自动授权normal权限,但是必须在App运行时请求每个dangerous权限。用户可以同意或拒绝每个权限,即使用户拒绝了某个权限的请求,App依然能够继续运行,只是部分功能无法使用。

注意:从Android6.0开始,用户能够在任何时候取消对某个App的权限授权,即使App target 一个低版本的SDK level。你应该充分测试App,不管target SDK level是什么,当缺少权限时,都要保证你的App运行正常。

这篇文章讲述了如何使用Android Support Library去检查、请求权限。虽然Android framework在Android 6.0(API 23)之后提供了类似的方法,但是使用这个Support Library更简单,因为你的App不需要在调用这些方法时检查App所运行设备的Android版本号。

2.1、Check For Permissions

如果你的App需要一个dangerous权限,你每次执行需要此权限的操作时,都必须检查是否拥有此权限。用户可以通过系统的应用设置界面,在任何时候取消对权限的授权,所以即使App昨天还可以使用相机,今天未必可以,除非获得了权限。

为了检查是否具有某个权限,调用 ContextCompat.checkSelfPermission() 方法。比如,下面的代码显示了如何检查当前Activity是否具有写日历的

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

App具有这个权限,上述方法会返回PackageManager.PERMISSION_GRANTED,然和App就可以执行这个操作了。如果App不具有这个权限,此防范会返回PackageManager.PERMISSION_DENIED,此App需要显示请求用户授权。

2.2、Request Permissions

如果的你App需要一个列在manifest中的dangerous权限,必须询问用户去同意这个权限。Android提供了好几个方法可以用来请求一个权限,调用这些方法会弹出一个标准的Android对话框,你无法定制的对话框。

2.2.1、Explain why the app needs permissions

在某些情况下,你可能想要用户知道为什么你的App需要一个权限。比如,如果用户打开了一个摄影App,用户不会对使用相机的权限意外,但是可能不理解为什么这个App需要访问用户的位置或者联系人。在你请求一个权限之前,你应该考虑给用户一个解释。需要注意的是,你别用解释淹没用户,如果你提供过多的解释,用户会觉得太罗嗦,然和卸载它。“解释就是掩饰”,这句话用在这里也合适。

如果用户之前拒绝了一个权限请求,但是再次使用需要请求此权限的功能,这可能表明用户不理解此功能为什么需要此权限,此时,进行解释是不错的方式。
为了帮助你发现需要解释的情形,Android提供了一个工具方法shouldShowRequestPermissionRationale(),如果一个App请求一个之前被拒绝的权限,这个方法会返回true。
注意:如果用户之前拒绝时勾选了“不再提示”,此方法返回false。if a device policy prohibits the app from having that permission,此方法总是返回false。

2.2.2、Request the permissions you need

如果你的App还没有获得需要的权限,App必须调用 requestPermissions()方法请求合适的权限,传入需要的权限和request code。这个方法是异步的,它立即就返回了,在用户响应后,系统调用回调方法返回结果,也会返回同样的request code。
下面的代码检查App是否具有读取用户联系人的权限,并且根据需要请求权限:

// 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.
    }
}

注意:当你的App调用requestPermissions(),系统会显示一个标准的对话框,一个你无法配置和改变的对话框。如果你需要向用户解释一些东西,在调用requestPermissions()之前做。请看“2.2.1、Explain why the app needs permissions”。

2.2.3、Handle the permissions request response

当你的App请求权限时,系统会提供一个对话框,让用户同意或拒绝。当用户操作后,系统会调用App的 onRequestPermissionsResult() 方法,返回用户的响应。你的App必须重写这个方法查看获得了哪些权限。这个回调返回和 requestPermissions() 一样的request code。比如,上面请求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
    }
}

系统显示的对话框描述了App请求的权限所属权限组的信息,它不会列出具体的权限。比如,你请求READ_CONTACTS权限,系统的对话框仅仅说你的App需要访问设备的联系人。用户只需要同意每个权限组的一个权限。如果你的App请求权限组的任何其它权限(列在manifest中),系统自动同意它们,当你请求去权限时,系统立即调用 onRequestPermissionsResult() 回调方法并返回PERMISSION_GRANTED,就像用户显示地点击了系统对话框的“同意”按钮。

注意:即使用户已经同意了权限组中的一个权限,你的App仍需要显示地请求需要的每个权限。此外,在未来的Android版本中,对于权限的分组可能会发生改变。你的代码必须基于权限不在相同的权限组这个假设,才能保证App运行正常。

比如,假设你在manifest中列了 READ_CONTACTS 和 WRITE_CONTACTS。你请求 READ_CONTACTS时用户同意了,当你请求 WRITE_CONTACTS 时,系统会自动同意而不是弹出对话框让用户操作。
如果用户拒绝了一个权限,你的App也应该执行合适的行为。比如,你的App可能需要显示一个对话框,解释为什么不能执行需要那个权限的功能。

当系统询问一个用户授权时,用户可能勾选“不再提示”。在这种情况下,你的App再次调用 requestPermissions() 请求权限时,系统立即拒绝并回调 onRequestPermissionsResult() 方法,返回 PERMISSION_DENIED,就像用户显示地拒绝了这个权限的请求。这意味着当你调用 requestPermissions(),你不能假设是用户直接拒绝了权限。


三、Normal and Dangerous Permissions

参考:https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous

Android系统的权限被分为好几个安全级别,两个最重要的安全级别是:normal和dangerous权限。

normal权限包括:你的App需要权限获取sandbox之外的数据/资源,但是对于用户的隐私和其它App的操作风险很小。比如,设置时区的normal权限。如果一个App声明需要一个normal权限,系统会自动授权给App。想了解所有的normal权限,请阅读“四、Normal Permissions”。

dangerous权限包括;你的App需要权限获取包括用户隐私的数据/资源,或者能够潜在影响用户存储的数据/其它App的行为。比如:读取用户的联系人就是dangerous权限。如果一个App声明需要一个dangerous权限,用户必须显示地授权给App,也就是手动授权。

3.1、Permission groups

所有的dangerous Android系统权限属于权限组。如果设备运行了Android 6.0(API 23),并且App的targetSdkVersion>=23,当App请求一个dangerous权限时,会发生下面的系统行为。

-如果一个App请求manifest列出的一个dangerous权限,并且App目前没有此权限组中的任何权限,系统会弹出一个对话框向用户描述App想获取的权限组信息。这个对话框不会描述特定的权限,比如,App请求READ_CONTACTS权限,对话框只会说明App需要访问设备的联系人。如果用户同意了,系统只会给App它请求的权限,也就是READ_CONTACTS。
-如果一个App请求manifest中的一个dangerous权限,并且次App已经具有相同权限组中的另一个dangerous权限,那么系统会立即同意此权限,不会弹出授权对话框。比如说:一个App已经具有READ_CONTACTS权限,那么当它请求WRITE_CONTACTS,系统会立即同意此权限。

任何权限都属于一个权限组,包括normal权限和你的App定义的权限。然而,只有dangerous权限的权限组会影响用户体验,你可以忽略normal权限的权限组。

如果设备运行在Android5.1(API 22)及以下,或者App的targetSdkVersion<=22,系统会在安装App时让用户授权App需要的权限,同样,系统只会提示用户App需要的权限组,而不是单个的权限。

下边列出了dangerous权限和所属的权限组。
这里写图片描述


四、Normal Permissions

参考:https://developer.android.com/guide/topics/security/normal-permissions.html

大多数的权限被指定为PROTECTION_NORMAL,这意味着让App拥有这些权限不会给用户的隐私和安全带来巨大的风险。
比如说,用户可能非常想知道为什么一个App要读取他的联系人信息,因此用户需要手动授权这些权限。相反地,让一个App去震动设备没有巨大的风险,所以这个权限被指定为normal。
如果一个App在manifest文件中声明需要使用一个normal权限,系统会在App安装阶段自动同意使用这个权限。系统不会要求用户去同意这个normal权限,用户也无法禁止App使用normal权限。

截至Android6.0,API23,以下的权限是PROTECTION_NORMAL:

ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS

五、Permissions Best Practices

参考:https://developer.android.com/training/permissions/best-practices.html#ask-neccessary

请求权限会让用户很烦恼,用户会抱怨你不断请求 dangerous 权限,然和避免使用你的App或者卸载它。下面的最佳实践帮助你避免这样糟糕的用户体验。

5.1、Consider Using an Intent

大多数情况下,你有两种方式执行一个任务。1、让你的App请求权限,然后自己执行。2、让你的App使用一个Intent,让其它App执行任务。

比如,假设你的App需要使用相机拍照。你的App可以请求 CAMERA 权限,这会允许的你的App直接控制相机。你的 App 可用使用 Camera APIs 控制相机并拍照。这种方式给你完全的控制,并且让你把拍照的UI合并在自己的App中。

当然,如果你不需要如此完全的控制,你可以使用 ACTION_IMAGE_CAPTURE intent,请求一张图片。当你发送这个 intent 时,系统会让用户选择一个相机 App(如果具有多个相机App)。然后,用户用选择的App拍照,最后通过你的App的 onActivityResult() 方法返回照片。

同样地,如果你需要打电话、访问联系人等等,你可以发送一个合适的intent,或者请求合适的权限直接执行任务。这两种方法都有优点和缺点。
1、如果你使用权限:
你的App对用户体验和执行的任务有着完全的控制。当然,如此广泛的控制会增加编码的复杂度,因为你需要设计一个合适的UI。
用户需要同意权限一次,在运行时或者安装时(根据用户设备的 Android 版本号)。之后,你的 App 能够顺畅地执行操作而不需要打扰用户。当然,如果用户拒绝了权限(或者在系统应用设置界面取消了),你的App就完全无法执行操作了。
2、如果你使用 intent:
你不需要设计UI,处理此 intent 的 App 会提供 UI,这就意味着你对于用户体验没有控制权,用户可能与一个你从未见过的App交互。
如果用户没有默认处理此 intent 的App,系统会提示用户选择一个App。如果用户没有设置一个默认处理此 intent 的App,用户需要每次选择一个App去执行操作。

5.2、Only Ask for Permissions You Need

每次你请求一个权限,都在迫使用户做一个决定。你应该减少这类请求发生的次数。
如果用户使用Android 6.0(API 23)及之后,每次用户尝试使用需要权限的App功能时,App必须请求权限从而打断用户。如果用户使用Android 5.1(API 22)及之前的版本,用户必须在安装App时同意所有权限;如果权限列表太长或看起来不合理,用户可能不会安装你的App。
基于这些原因,你应该最小化请求的权限。

通常,你的App可用使用 intent 去避免请求权限。如果一个功能不是App的核心部分,你应该考虑让其它App去处理,请参考“5.1、 Consider Using An Intent”。

5.3、Do not Overwhelm the User

如果用户使用 Android 6.0(API 23)及之后的设备,用户必须在运行时同意权限请求。如果你让用户一次面对太多的权限请求,你可能会让用户茫然并退出你的App。因此,你应该在需要的时候请求权限。

在某些时候,一个或多个权限对你的App来讲是绝对需要的。你有必要在App启动时尽快请求这些权限。比如,如果你制作一个摄影App,App需要访问设备的相机,当用户第一次启动App时,他们不会对请求使用相机感到意外。但,如果此App通知具有分享图片给联系人的功能,你不应该在第一次启动时就请求此权限,而是在用户使用分享功能时请求权限。

如果你的App提供了引导页,你可以在引导页结束时请求必须的权限。

5.4、Explain Why You Need Permissions

当你调用 requestPermissions() 方法时,系统弹出的权限对话框会显示你需要的权限组信息,但是不会说明原因。在某些情况下,用户会感到困惑。因此,在调用 requestPermissions() 方法之前,可用解释一下为什么需要此权限。

比如,一个摄影App想要位置服务来标记图片的地理位置。一个典型的用户不会理解一张相片会包括位置信息,因此会对摄影App想获取位置信息感动困惑。在这种情况下,在调用 requestPermissions() 方法之前,解释一下为什么需要此权限是不错的方式。

在引导页中告知用户需要同意这些权限是不错的方式。引导页能够依次显示App的每个功能,引导结束时,可用解释需要哪些权限。比如,这个摄影App的引导页可以展示它的“向联系人分享图片”功能,然和告诉用户需要查看联系人的权限,然后这个App可以调用 requestPermissions() 去请求权限。当然,不是每个用户会看完引导页,所以你依然需要在App运行时检查和请求权限。

5.5、Test for Both Permissions Models

从Android 6.0(API 23)开始,用户可以在运行时同意/拒绝权限,而不是在安装App时。因此,你必须在各种情况下测试你的App。在Android 6.0之前,你可以假设你的App可以完全运行,它具有manifest中声明的所有权限。可是在新的权限模式下,你不能再做这个假设了。

下面的提示将会帮助你定位权限相关的代码问题,当App运行在API 23及以上时。
1、定位你App当前的权限和使用权限的相关代码。
2、执行需要权限的功能。
3、测试各种权限授权、取消授权的组合。比如,一个相机App可能在manifest中列出CAMERA、READ_CONTACTS、ACCESS_FINE_LOCATION。你应该测试这些权限开启/关闭时,你的App能否正常执行。记住,从Android 6.0开始,用户可以在系统设置中随时改变权限,即使你的App target API 22及更低。
4、使用adb工具的命令行去管理权限:

List permissions and status by group:
$ adb shell pm list permissions -d -g

Grant or revoke one or more permissions:
$ adb shell pm [grant|revoke] <permission-name> …

5、分析你的App,看看那些需要权限的功能有没有问题。


六、广告

  • 手机如何连电脑?
  • 如何与电脑互发文件?
  • 如何与电脑互发文字?
  • 如何推送通知到电脑?
  • 电脑如何远程控制手机的相机?

快牙网传——全部搞定!

不想试试吗?
在豌豆荚等应用商店搜索“快牙网传“,或立即下载 Apk,也可以扫码下载。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值