Android 6
CALENDAR(日历)
* READ\_CALENDAR
* WRITE\_CALENDAR
CAMERA(相机)
* CAMERA
CONTACTS(联系人)
* READ\_CONTACTS
* WRITE\_CONTACTS
* GET\_ACCOUNTS
LOCATION(位置)
* ACCESS\_FINE\_LOCATION
* ACCESS\_COARSE\_LOCATION
MICROPHONE(麦克风)
* RECORD\_AUDIO
PHONE(手机)
* READ\_PHONE\_STATE
* CALL\_PHONE
* READ\_CALL\_LOG
* WRITE\_CALL\_LOG
* ADD\_VOICEMAIL
* USE\_SIP
* PROCESS\_OUTGOING\_CALLS
SENSORS(传感器)
* BODY\_SENSORS
SMS(短信)
* SEND\_SMS
* RECEIVE\_SMS
* READ\_SMS
* RECEIVE\_WAP\_PUSH
* RECEIVE\_MMS
STORAGE(存储卡)
* READ\_EXTERNAL\_STORAGE
* WRITE\_EXTERNAL\_STORAGE
使用 adb 命令可以查看这些需要授权的权限组:
adb shell pm list permissions -d -g
1
使用 adb 命令同样可以授权/撤销某个权限:
adb shell pm [grant|revoke] <permission-name>...
1
关于运行时权限的一些建议
============
只请求你需要的权限,减少请求的次数,或用隐式 Intent 来让其他的应用来处理。
如果你使用 Intent,你不需要设计界面,由第三方的应用来完成所有操作。比如打电话、选择图片等。
如果你请求权限,你可以完全控制用户体验,自己定义 UI。但是用户也可以拒绝权限,就意味着你的应用不能执行这个特殊操作。
防止一次请求太多的权限或请求次数太多,用户可能对你的应用感到厌烦,在应用启动的时候,最好先请求应用必须的一些权限,非必须权限在使用的时候才请求,建议整理并按照上述分类管理自己的权限:
普通权限(Normal PNermissions):只需要在 Androidmanifest.xml 中声明相应的权限,安装即许可。
需要运行时申请的权限(Dangerous Permissions):
必要权限:最好在应用启动的时候,进行请求许可的一些权限(主要是应用中主要功能需要的权限)。
附带权限:不是应用主要功能需要的权限(如:选择图片时,需要读取 SD 卡权限)。
解释你的应用为什么需要这些权限:在你调用
requestPermissions()
之前,你为什么需要这个权限。例如,一个摄影的 App 可能需要使用定位服务,因为它需要用位置标记照片。一般的用户可能会不理解,他们会困惑为什么他们的 App 想要知道他的位置。所以在这种情况下,所以你需要在
requestpermissions()
之前告诉用户你为什么需要这个权限。使用兼容库
support-v4
中的方法
ContextCompat.checkSelfPermission()
ActivityCompat.requestPermissions()
ActivityCompat.shouldShowRequestPermissionRationale()
1
2
3
几个重要的方法与常量解释
============
PackageManager 中的两个常量:
PackageManager.PERMISSION_DENIED:该权限是被拒绝的。
PackageManager.PERMISSION_GRANTED:该权限是被授权的。
Activity 中或者 Fragment 都会有以下几个方法:
int checkSelfPermission(String)
void requestPermissions(int, String...)
boolean shouldShowRequestPermissionRationale(String)
void onRequestPermissionsResult()
1
2
3
4
上述四个方法中,前三个方法在support-v4
的ActivityCompat
中都有,建议使用兼容库中的方法。最后一个方法是用户授权或者拒绝某个权限组时系统会回调 Activity 或者 Fragment 中的方法。
checkSelfPermission() 检查权限
检查某一个权限的当前状态,你应该在请求某个权限时检查这个权限是否已经被用户授权,已经授权的权限重复申请可能会让用户产生厌烦。
该方法有一个参数是权限名称,有一个 int 的返回值,用这个值与上面提到的两个常量做比较可判断检查的权限当前的状态。
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// 没有权限,申请权限。
}else{
// 有权限了,去放肆吧。
}
1
2
3
4
5
6
requestPermissions() 申请权限
请求用户授权几个权限,调用后系统会显示一个请求用户授权的提示对话框,App 不能配置和修改这个对话框,如果需要提示用户这个权限相关的信息或说明,需要在调用 requestPermissions() 之前处理,该方法有两个参数:
int requestCode,会在回调
onRequestPermissionsResult()
时返回,用来判断是哪个授权申请的回调。String[] permissions,权限数组,你需要申请的的权限的数组。
由于该方法是异步的,所以无返回值,当用户处理完授权操作时,会回调 Activity 或者 Fragment 的
onRequestPermissionsResult()
方法。
对于 Activity 我们直接调用requestPermissions(int, String[])
即可,不过这个方法是在 api leve 23 以上,所以我们为了适配可以是使用兼容包提供的方法:
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.READ_CONTACTS}, MMM);
1
对于 support 包的Fragment
就可以直接调用requestPermissions(int, String[])
,对于 app 包的Fragment
就需要做版本判断了,这样就显得比较麻烦。
onRequestPermissionsResult() 处理权限结果回调
该方法在 Activity/Fragment 中应该被重写,当用户处理完授权操作时,系统会自动回调该方法,该方法有三个参数:
int requestCode,在调用
requestPermissions()
时的第一个参数。String[] permissions,权限数组,在调用
requestPermissions()
时的第二个参数。int[] grantResults,授权结果数组,对应 permissions,具体值和上方提到的 PackageManager 中的两个常量做比较。
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MMM: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限被用户同意,可以去放肆了。
} else {
// 权限被用户拒绝了,洗洗睡吧。
}
return;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
shouldShowRequestPermissionRationale()
望文生义,是否应该显示请求权限的说明。
第一次请求权限时,用户拒绝了,调用
shouldShowRequestPermissionRationale()
后返回 true,应该显示一些为什么需要这个权限的说明。用户在第一次拒绝某个权限后,下次再次申请时,授权的 dialog 中将会出现“不再提醒”选项,一旦选中勾选了,那么下次申请将不会提示用户。
第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项,调用
shouldShowRequestPermissionRationale()
后返回 false。设备的策略禁止当前应用获取这个权限的授权:
shouldShowRequestPermissionRationale()
返回 false 。加这个提醒的好处在于,用户拒绝过一次权限后我们再次申请时可以提醒该权限的重要性,免得再次申请时用户勾选“不再提醒”并决绝,导致下次申请权限直接失败。
综上所述,整合代码后:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
// 没有权限。
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) {
// 用户拒绝过这个权限了,应该提示用户,为什么需要这个权限。
} else {
// 申请授权。
ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MMM);
}
}
...
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MMM: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限被用户同意,可以去放肆了。
} else {
// 权限被用户拒绝了,洗洗睡吧。
}
return;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
总结
--
从上面来看,判断很多,逻辑也很多,这样就加重了我们开发的负担,加上很多人反馈说国产手机有各种各样的 bug,这样兼容起来就更加麻烦了,那么下面我就为大家介绍一个开源内裤来解决这一系列问题。
AndPermission
=============
这个开源库名叫 AndPermission:https://github.com/yanzhenjie/AndPermission,经过我的实践是完全解决了上述问题,推荐大家使用。
Gradle
compile 'com.yanzhenjie:permission:1.0.6'
1
Maven
<dependency>
<groupId>com.yanzhenjie</groupId>
<artifactId>permission</artifactId>
<version>1.0.5</version>
<type
pom</type>
</dependency>
1
2
3
4
5
6
Eclipse 请放弃治疗
使用介绍
====
我建议看官去 Github 下载Demo
并阅读本文会帮助你理解。
申请权限
// 在 Activity:
AndPermission.with(activity)
.requestCode(100)
.permission(Manifest.permission.WRITE_CONTACTS)
.rationale(...)
.callback(...)
.start();
// 在 Fragment:
AndPermission.with(fragment)
.requestCode(100)
.permission(
// 多个权限,以数组的形式传入。
Manifest.permission.WRITE_CONTACTS,
Manifest.permission.READ_SMS
)
.rationale(...)
.callback(...)
.start();
// 在其它任何地方:
AndPermission.with(context)
.requestCode(100)
.permission(
Manifest.permission.WRITE_CONTACTS,
Manifest.permission.READ_SMS
)
.rationale(...)
.callback(...)
.start();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
接受回调结果
接受回调结果目前有两种方式:一、Listener
方式,二、注解方式。
方式一:Listener 方式回调
在callback()
方法传入PermissionListener
即可,授权成功或者失败至少会回调其中一个方法。
AndPermission.with(context)
...
.requestCode(200)
.callback(listener)
.start();
private PermissionListener listener = new PermissionListener() {
@Override
public void onSucceed(int requestCode, List<String> grantedPermissions) {
// 权限申请成功回调。
// 这里的 requestCode 就是申请时设置的 requestCode。
// 和 onActivityResult()的 requestCode 一样,用来区分多个不同的请求。
if(requestCode == 200) {
// TODO ...
}
}
@Override
public void onFailed(int requestCode, List<String> deniedPermissions) {
// 权限申请失败回调。
if(requestCode == 200) {
// TODO ...
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
方式二:注解方式回调
在callback()
方法传入你的回调方法所在实例的对象即可。
AndPermission.with(context)
...
.requestCode(300)
.callback(this)
.start();
// 成功回调的方法,用注解即可,这里的 300 就是请求时的 requestCode。
@PermissionYes(300)
private void getPermissionYes(List<String> grantedPermissions) {
// TODO 申请权限成功。
}
@PermissionNo(300)
private void getPermissionNo(List<String> deniedPermissions) {
// TODO 申请权限失败。
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如果你会用了,你就可以大刀阔斧的干了,博客中讲到的各种复杂逻辑,AndPermission
自动完成。
Rationale 能力
Android
运行时权限有一个特点,在拒绝过一次权限后,再此申请该权限,在申请框会多一个**[不再提示]的复选框,当用户勾选了[不再提示]**并拒绝了权限后,下次再申请该权限将直接回调申请失败。
因此Rationale
功能是在用户拒绝一次权限后,再次申请时检测到已经申请过一次该权限了,允许开发者弹窗说明申请权限的目的,获取用户的同意后再申请权限,避免用户勾选不再提示,导致不能再次申请权限。
方式一:使用 AndPermssion 默认 MD 风格对话框
AndPermission.with(this)
...
.requestCode(...)
.rationale((requestCode, rationale) ->
// 此对话框可以自定义,调用 rationale.resume()就可以继续申请。
AndPermission.rationaleDialog(context, rationale).show()
)
.start()
1
2
3
4
5
6
7
8
方式二:自定义对话框
AndPermission.with(this)
...
.requestCode(...)
.rationale(rationaleListener)
.start()
/**
Rationale 支持,这里自定义对话框。
*/
private RationaleListener rationaleListener = (requestCode, rationale) -> {
AlertDialog.newBuilder(this)
.setTitle("友好提醒")
.setMessage("你已拒绝过定位权限,沒有定位定位权限无法为你推荐附近的妹子,你看着办!")
.setPositiveButton("好,给你", (dialog, which) -> {
rationale.resume();
})
.setNegativeButton("我拒绝", (dialog, which) -> {
rationale.cancel();
}).show();
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
评论