Android 请求应用权限

一、概述

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

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

  • 正常权限不会直接给用户隐私权带来风险。如果在清单中列出了正常权限,系统将自动授予该权限。

  • 危险权限会授予应用访问用户机密数据的权限。如果在清单中列出了危险权限,则用户必须明确批准使用这些权限。

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

  • 如果设备运行的是 Android 5.1 或更低版本,或者应用的目标 SDK 为 22 或更低:如果清单中列出了危险权限,则用户必须在安装应用时授予此权限;如果他们不授予此权限,应用无法安装。

  • 如果设备运行的是 Android 6.0 或更高版本,或者应用的目标 SDK 为 23 或更高:应用必须在清单中列出权限,并且它必须在运行时请求其需要的每项危险权限。用户可以授予或拒绝每项权限,且即使用户拒绝权限请求,应用仍可以继续运行有限的功能。

二、检查权限

如果你的应用需要危险权限,则每次执行需要这一权限的操作时你都必须检查自己是否具有该权限。因为用户可以随时更改权限的授权状态。

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

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

如果应用具有此权限,方法将返回 PackageManager.PERMISSION_GRANTED,并且应用可以继续操作。如果应用不具有此权限,方法将返回 PERMISSION_DENIED

还有一点,如何判断当前 Android 版本号是否大于某版本,这也是判断我们是否需要动态申请权限的基准。下面是个例子:
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {  
            //  大于等于 24 即为 7.0 及以上执行内容
        } else {
            //  低于 24 即为 7.0 以下执行内容
  }

三、请求权限

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

1. 解释应用为什么需要权限

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

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

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

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

注:如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don't ask again 选项,此方法将返回 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() 之前进行,如解释应用为什么需要权限。

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

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

例如,假设你在应用清单中列出了 READ_CONTACTS 和 WRITE_CONTACTS。如果你请求 READ_CONTACTS 且用户授予了此权限,那么,当你请求 WRITE_CONTACTS 时,系统将立即授予你该权限,不会与用户交互。

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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容