SAKA'S BLOG

(译)安卓系统权限

上一篇讲解了我自己开发中gradle的配置。

谷歌自从更新6.0以后,更改了权限管理的方式,在6以上的版本中采用全新的动态权限管理,我作为一个迟钝的程序员,始终把版本设定在22,规避这个动态权限获取的问题。今天我就要好好搞一搞,弄期初这个是怎么回事。
今天的翻译包含以下内容

  • 声明权限
  • 在运行时请求权限
  • 权限最佳做法

声明权限

每个安卓应用在访问受限的沙盒中运行,如果需要使用沙盒外的信息或者资源,则必须请求相应权限才可以。声明权限在manifest文件中声明。

1
2
3
4
5
6
 <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>

这个非常简单,就是添加(有一些时候可以不使用权限而使用intent来获取系统功能)

权限又分为正常权限和危险权限

官方文档是这样定义的

  • 正常权限:涵盖应用需要访问其沙盒以外的数据或者资源,但对用户隐私或其他应用操作风险很小的区域。应用声明其需要正常权限,系统会自动向应用授权该权限。

  • 危险权限:涵盖应用需要设计用户隐私信息的数据或者资源,或可能对用户存储的数据或其他应用的操作产生影响的区域。如果应用声明其需要危险权限,则必须经过用户同意方可。

权限组

所有危险权限都属于权限组。

  • 如果权限请求中有危险权限,而应用目前没有获得用户授予该权限,则系统会弹出提示框,描述请求权限所在的权限组,征求用户是否同意。
  • 如果应用请求其清单中列出的危险权限,而在同意权限组中以经过用户同意,则不需要在与用户交互,直接授予该权限。

    用户申请的单个权限信息会获得整个权限组的权限,不需要重复申请。

下面的表中列出了危险权限和权限组

权限组 权限
CALENDAR READ_CALENDAR
CALENDAR WRITE_CALENDAR
CAMERA CAMERA
CONTACTS READ_CONTACTS
CONTACTS WRITECONTACTS
CONTACTS GET_ACCOUNTS
LOCATION ACCESS_FINE_LOCATION
LOCATION ACCESS_COARSE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE READ_PHONE_STATE
PHONE CALL_PHONE
PHONE READ_CALL_LOG
PHONE WRITE_CALL_LOG
PHONE ADD_VOICEMAIL
PHONE USE_SIP
PHONE PROCESS_OUTGOING_CALLS
SENSORS BODY_SENSORS
SMS SEND_SMS
SMS RECEIVE_SMS
SMS READ_SMS
SMS RECEIVE_WAP_PUSH
SMS RECEIVE_MMS
STORAGE READ_EXTERNAL_STORAGE
STORAGE WRITE_EXTERNAL_STORAGE

一些特殊权限我没有列出来,这里就不写了

检查权限

若果你的应用需要危险权限,则每次之赐你个这一权限的操作时你都必须检查自己是否拥有该权限。因为用户可以自由更改权限,今天你获取了不代表明天还继续拥有。
ContextCompat.checkSelfPermission()
该方法是安卓V4支持库中提供的检测权限的方法
先不去管ContextCompat类,直接查看checkSelfPermission()方法
int checkSelfPermission (Context context, String permission)
permission是你要检查权限的名称。
该方法返回一个int型,

  • PERMISSION_GRANTED 表示你拥有该权限,可以继续执行操作
  • PERMISSION_DENIED 表示你没有改权限,你这时候需要向用户申请权限

    解释为什么需要权限

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

    Tips: 如果用户在过去拒绝了权限请求,并选择了Don’t ask again 选项,此方法返回false。若果设备规范禁止应用具有该权限,此方法也将返回false。

下面看一下这个方法的api,
boolean shouldShowRequestPermissionRationale (Activity activity, String permission)
你只能在你没有获取权限的情况下显示这个ui。

请求您需要的权限

如果应用未获取权限,你需要调用一个方法来请求—- requestPermission();
这个方法异步执型:它会立即返回,并且在用户相应对话框之后,系统会使用结果调用应用的回调方法,将应用传递的相同请求码传递到requestPermission().
举个例子,请求联系人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 当前需要申请权限的activity
//如果未获得该权限
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {

//是否显示解释性对话框
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
//异步显示一个解释性对话框,等待用户回应,不要锁死这个线程!
//当用户看待解释后,再次尝试获取权限。
} else {

//不需要显示解释性对话框,直接申请权限,这时候会弹出权限申请对话框
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
//此方法将会回调到onRequestPermissionsResult方法中
}
}

弹出的权限申请对话框是不能修改的。

处理权限请求相应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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 {
// 权限被拒绝了
}
return;
}
}
}

本期权限就这么多,下期讲一讲 深入的东西和我自己的权限封装