Android-系统权限的前世今生

时间:2022-02-03 06:18:04

Android-系统权限的前世今生

简述

自从Android6.0面世之后,Android中的权限要求就更加严格和灵活了,6.0系统之前,一个app应用所需的权限,只需要在其安装的时候一次性向用户请求完毕即可,但是6.0之后,应用的权限改为了运行时权限。我们这篇文章,就来看一下,我们在开发中,需要的基本的权限知识。

Android权限等级

Android的系统权限分为几个保护级别,从大体上来讲,可以分为两个保护级别,那就是正常权限危险权限。我们来分别看一下这两种权限:

正常权限

正常权限涵盖应用需要访问其沙盒外部数据或资源,但对用户隐私或其他应用操作风险很小的区域。例如,设置时区的权限就是正常权限。如果应用声明其需要正常权限,系统会自动向应用授予该权限。

这是Android文档里面对正常权限的定义,意思就是,正常权限,就是那些无关用户隐私的权限,比如改改系统时间这些请求,就不会对用户的隐私造成威胁,这些权限被视为正常权限。正常权限的使用只要应用在其应用清单文件中申请了,就能够直接获得。

危险权限

危险权限涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如,能够读取用户的联系人属于危险权限。如果应用声明其需要危险权限,则用户必须明确向应用授予该权限。

上面是Android文档中对危险权限的定义,我们可以看到,危险权限就是涉及那些可能会对用户隐私造成威胁的行为,比如读取联系人信息了,发送短信了。对于这些权限,在应用清单文件中申请权限只是必要条件,却不是充分条件,因为我们需要在应用运行的时候,动态的去和用户申请权限。

权限组

我们在使用Android的权限时,应该有过以下场景:那就是有些权限,我们申请用户同意之后,和这些权限关系比较“紧密”的一些权限,不用申请就能被通过了。难道这是Android里面的bug?并不是,这就牵扯到Android权限组的概念了。

Android里面,所有的权限,包括正常权限、危险权限、应用自定义的权限,都是属于权限组的。比如CALENDAR权限组,就包括READ_CALENDAR和WRITE_CALENDAR两个权限。但是当权限是正常权限的时候,权限组并不会影响用户的体验。

当权限是危险权限的时候,从Android6.0系统开始,系统会有一下两个行为:

  1. 如果应用请求其清单中列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求 READ_CONTACTS 权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。
  2. 如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请求 WRITE_CONTACTS,系统将立即授予该权限。

没错,只要你的权限组里面有一个权限被同意过,你就可以直接使用这个权限组里面其他的权限了。

Android权限前后版本变化

我们题目中说道,是系统权限的前世今生,为什么这么说呢?那是因为,随着Android6.0系统的发布,系统对于权限的控制,有了一个很大的变化,那就是出现了运行时权限,下面我们来看一下,何为运行时权限。

如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在安装时要求用户授予权限。再次强调,系统只告诉用户应用需要的权限组,而不告知具体权限。

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

我们可以看到,以6.0系统和Api23为分割线,之前的时候应用需要的权限是在安装的时候一次性请求完毕的,但是6.0和API23之后,就不是在安装的时候请求权限了,而是改为了运行时请求权限。可能很多人有疑问,那就是,为什么我用的6.0系统,情况却不是这样呢?那是因为你忽略了一个终于的条件,那就是这个划分,是关注你应用的 targetSdkVersion的。

权限的申请

前面,我们一起看了一下权限的等级划分,以及随着版本的变化,各版本权限的重要变化,下面,我们就要具体看一下,怎样,去申请一个应用需要的权限。

具体来说,一个应用要申请权限,需要分为两个步骤,首先,首先,需要在应用的清单文件中申请权限,里面包括所有涉及到的正常权限和危险权限,正常权限安卓会自动授权,危险权限的话,就要涉及到下一步。其次,对于危险权限,系统需要在运行时需要此权限的时候,向用户发出申请。我们把这两个步骤称为声明权限和运行时权限请求。

下面,我们来分别看一下这两个步骤:

声明权限

我们来看一个Demo:

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

上面,就是一个应用的权限声明,我们需要把 元素置于应用清单中。权限声明很简单,就是在应用清单文件中,把我们应用所需要的所有权限,都列在上面,系统在您声明权限之后的行为取决于权限的敏感性。如果权限不影响用户隐私权,系统会自动授权。如果权限可能涉及对敏感用户信息的访问,系统将要求用户审批请求。注意:对于正常权限,虽然系统会自动授权,但是仍然要在应用清单文件中声明,而对于危险权限,虽然系统不会直接授权,但是你仍然需要在应用清单文件中声明,因为你需要告诉系统你的应用可能会用到此权限。

运行时权限请求

运行时权限请求就要比声明权限麻烦多了,需要分为以下几个步骤:

  1. 检查权限
  2. 请求权限
  3. 处理权限请求响应

下面,我们来分别看一下这几个步骤:

检查权限

我们先来看一个Demo:

// 假设thisActivity是当前的Activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,Manifest.permission.WRITE_CALENDAR);

以上代码就是一个请求权限的操作,我们需要调用ContextCompat.checkSelfPermission() 方法,此方法需要两个参数,一个是当前Activity的上下文,一个是我们清单中已经声明好的权限。这样,我们就能够判断出是否拥有此权限。
注意:我们需要在每一次使用危险权限的时候进行检查,因为我们不能确定今天申请到的权限,明天是否还有效。

请求权限

我们来看一个Demo:

if (ContextCompat.checkSelfPermission(thisActivity,Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED) {
// 当用户拒绝过我们的权限,且没有点击类似”下次不要想我询问“这种对话框 // 的时候,shouldShowRequestPermissionRationale方法就会返回 // true,否则,返回false,我么可以利用这个特性,在用户拒绝过一次后,向用 // 户解释一下权限对于此应用的重要性
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) {
Dialog alertDialog = new AlertDialog.Builder(this)
.setMessage("此应用必须拥有读取联系人信息权限,否则不能正常运行!")
.setPositiveButton("关闭", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_CONTACTS},
requestCode);
}
}).create();
alertDialog.show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS},
requestCode);
}

我们来看一下运行效果:

第一次运行的时候,应用检查没有权限,申请权限,我们拒绝
Android-系统权限的前世今生

我们关系应用,再次打开,这个时候检查发现没有权限,继续申请权限,并且发现之前拒绝过权限,shouldShowRequestPermissionRationale方法返回true,开始提示用户权限的重要性,如图所示:
Android-系统权限的前世今生

然后,我们点击关闭,继续弹出权限请求框
Android-系统权限的前世今生

这个时候的权限请求框多了一个选择,那就是Nerver ask again,如果我们点击了这个选项,那么如我我们这次拒绝权限,下次启动的时候,shouldShowRequestPermissionRationale方法就不会再返回true了,而是返回false,也就是意味着,我们的程序永远不可能再主动申请权限了,除非用户自己去系统设置里面给我们设置权限,所以,必要的时候,如果申请不到权限,而且此权限影响到系统的正常使用,那么我们就应该在用户拒绝的情况下拒绝打开app。

处理请求结果

运行时权限的请求已经来到了最后一步,那就是我们应该处理权限的请求结果。权限可能请求成功,也可能请求失败,这两种情况我们都需要进行处理。

@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// 如果请求被拒绝,那么结果肯定为空
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//请求成功,做该做的去吧
} else {

//请求失败,根据权限对应用的重要性,我们应该觉得是否 //继续运行app的相关功能
}
return;
}
}
}

针对上面的代码,我们有几点需要说的:
1. requestCode,是什么呢,requestCode就是我们请求权限的对话框的回调码,也就是我们申请权限那个方法里面的requestCode参数。我们需要用这个码,来确定是哪一个对话框的回调执行。
2. grantResults.length,我们为什么需要判断一下length大小呢,原因是我们可以在申请权限的时候,一次性申请多个权限,我们可以看到申请权限的方法里面对于权限的参数,是一个String[]类型的,我们可以一次性把我们需要的权限都填进去,所以,在处理结果的时候,我们需要判断一下具体是哪一个被我们申请的权限。

##总结

到此为止,我们对与Android权限的基础学习就已经结束了,文章只介绍了开发中我们需要的权限方面的基础内容,大家可以自行查找相关稳定,进行深入的学习。

转载请注明地址:http://blog.csdn.net/linukey/article/details/64438474