原文地址:https://www.2cto.com/kf/201609/544790.html
android 6.0 有些危险权限需要运行时手动获取才能继续执行应用,否则将出来很多崩溃问题
如果sdk设置22是可以在启动时避免这个问题,先获取到全部的权限,但是一旦用户关闭了你需要的权限,呢完了。你的程序还是奔溃了。用户如果不知道是因为自己关闭了权限导致应用奔溃的。呢就陷入死循环的奔溃中,所以还是先加上这个控制比较好。
以下是通过android系统提供的在运行时获取权限的办法,这里我就不写了网上也有不少我就复制来给大家看看。大家看过了解后我会给大家推荐一个我现在正在使用并且感觉比较好的一款权限管理框架。
当你的这个activity某个功能需要调用到权限的时候(比如,相机,文件读写) 你需要在调用功能前先去检查下权限
1
2
3
4
5
6
|
if
(ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
}
else
{
//这是已经获得到权限 可以写你的逻辑了
}
|
这里涉及到一个API,ContextCompat.checkSelfPermission,主要用于检测某个权限是否已经被授予,方法返回值为
PackageManager.PERMISSION_DENIED或者
PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了。
这是运行时去获得授权的方法
1
2
3
|
<code><code><code>ActivityCompat.requestPermissions(thisActivity,
new
String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);</code></code></code>
|
该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。
处理权限申请回调
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<code><code><code><code>
@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
;
}
}
}
</code></code></code></code>
|
如果用户拒绝了你的请求,但是没有选择不再提示
1
2
|
<code><code><code><code>
if
(ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) </code></code></code></code>
|
这个API可以让你在次请求的时候,判断是不是已经拒绝过一次了,如果已经拒绝了这是你可以在这里给用户一些提示,为什么需要这个权限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<code><code><code><code>
if
(ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
if
(ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// 写上你的提示。然后再去请求权限,比如一个提示框,然后告诉用户为什么需要这个权限,用户点击确定后再去调用ActivityCompat请求一遍权限
}
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.
}
}</code></code></code></code>
|
系统的方法就讲到这里。系统的权限请求 activity和fragment略有不同。
1
2
3
4
|
<code><code><code><code>
//activity
ActivityCompat.requestPermissions(conent, perms, requestCode);
//fragment
fragment.requestPermissions(perms, requestCode);</code></code></code></code>
|
以上的代码 鸿神的博客写的比较详细大家可以去鸿神呢里看看 传送门 https://blog.csdn.net/lmj623565791/article/details/50709663
鸿神也在后边详解了两个框架 PermissionGen 和 MPermissions 第一个是用反射实现的,但是这两个框架的代码稍稍多了一点,对我来说不宜学习。所以我推荐一个更简洁的框架,这个只需要一个类,一个注解就决绝了以上的问题。
这个框架就是
EasyPermission 大家也可以去网上找找这个框架的使用,我在这里就简单的介绍一下使用
activity或者fragment需要实现这个接口
1
|
<code><code><code><code>EasyPermissions.PermissionCallbacks</code></code></code></code>
|
1
2
3
4
5
6
7
8
9
10
|
<code><code><code><code>
@Override
public
void
onPermissionsGranted(
int
requestCode, List<string> perms) {
//这是回掉成功的方法,成功的需要判断请求成功几条权限,可以用list的条数进行判断 请求的条数等于成功数这样就是全部请求成功了
}
@Override
public
void
onPermissionsDenied(
int
requestCode, List<string> perms) {
//这是失败的
}
}</string></string></code></code></code></code>
|
这是请求时的代码
1
2
3
4
5
6
7
8
|
<code><code><code><code>
@AfterPermissionGranted
(REQUECT_CODE_SDCARD)
//请求时的请求码
private
void
getPermiss(){
if
(EasyPermissions.hasPermissions(
this
,
new
String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE})) {
}
else
{
EasyPermissions.requestPermissions(
this
,
"没有权限将无法继续执行,请为应用授权!"
, REQUECT_CODE_SDCARD, permission);
}
}</code></code></code></code>
|
然后就是系统的回掉方法,你需要调用框架的回掉方法,把系统的回掉方法的值传给它
1
2
3
4
5
|
<code><code><code><code>
@Override
public
void
onRequestPermissionsResult(
int
requestCode,
@NonNull
String[] permissions,
@NonNull
int
[] grantResults) {
super
.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults,
this
);
</code></code></code></code>
|
这样一个权限请求就写完了。当你的应用需要用到这些权限的时候。调用getPermiss方法就可以。
后面我会复上这个框架的类。
现在框架都讲完了。其实会遇到一个问题,呢就是如果用户点了拒绝,然后也选择了不再提醒,但是我们的应用有必须需要这个权限,呢怎么办呢,其实可以在框架中稍稍修改下就可以解决这个问题,当不能再弹出来提示框的时候,我们可以给一个提示框,然后让用户到系统设置我们应用的界面里,去手动的打开这个权限:
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
<code><code><code><code>
private
static
int
count;
//定义一个记录请求次数的值
public
static
void
requestPermissions(
final
Object object, String rationale,
@StringRes
int
positiveButton,
@StringRes
int
negativeButton,
final
int
requestCode,
final
String... perms) {
checkCallingObjectSuitability(object);
final
PermissionCallbacks callbacks = (PermissionCallbacks) object;
boolean
shouldShowRationale =
false
;
for
(String perm : perms) {
shouldShowRationale = shouldShowRationale || shouldShowRequestPermissionRationale(object, perm);
}
if
(shouldShowRationale) {
//用户虽然点了拒绝,但是没有选择不再提示,这样每次都会弹这个框框
AlertDialog dialog =
new
AlertDialog.Builder(getActivity(object))
.setMessage(rationale)
.setPositiveButton(positiveButton,
new
DialogInterface.OnClickListener() {
@Override
public
void
onClick(DialogInterface dialog,
int
which) {
executePermissionsRequest(object, perms, requestCode);
}
})
.setNegativeButton(negativeButton,
new
DialogInterface.OnClickListener() {
@Override
public
void
onClick(DialogInterface dialog,
int
which) {
// act as if the permissions were denied
callbacks.onPermissionsDenied(requestCode, Arrays.asList(perms));
}
}).create();
dialog.show();
}
else
{
if
(count==
0
){
//第一次请求
executePermissionsRequest(object, perms, requestCode);
count=
1
;
}
else
{
//如果选择了不再提示,呢样就会进入这里,弹出提示框,让他直接去系统设置中打开权限
AlertDialog dialog =
new
AlertDialog.Builder(getActivity(object))
.setMessage(perminssions_content)
.setPositiveButton(positiveButton,
new
DialogInterface.OnClickListener() {
@Override
public
void
onClick(DialogInterface dialog,
int
which) {
Uri packageURI = Uri.parse(
"package:"
+ getActivity(object).getPackageName().toString());
Intent intent =
new
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
getActivity(object).startActivity(intent);
}
})
.setNegativeButton(negativeButton,
new
DialogInterface.OnClickListener() {
@Override
public
void
onClick(DialogInterface dialog,
int
which) {
dialog.dismiss();
}
}).create();
dialog.show();
}
}
}</code></code></code></code>
|
要记得权限请求成功后,吧count还原成0,要不别的页面就不能正常请求了。在这个方法里边还原
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
31
32
33
34
35
|
<code><code><code><code>
public
static
void
onRequestPermissionsResult(
int
requestCode, String[] permissions,
int
[] grantResults, Object object) {
checkCallingObjectSuitability(object);
PermissionCallbacks callbacks = (PermissionCallbacks) object;
// Make a collection of granted and denied permissions from the request.
ArrayList<string> granted =
new
ArrayList<>();
ArrayList<string> denied =
new
ArrayList<>();
for
(
int
i =
0
; i < permissions.length; i++) {
String perm = permissions[i];
if
(grantResults[i] == PackageManager.PERMISSION_GRANTED) {
granted.add(perm);
}
else
{
denied.add(perm);
}
}
// Report granted permissions, if any.
if
(!granted.isEmpty()) {
// Notify callbacks
count=
0
;
//请求成功,还原count
callbacks.onPermissionsGranted(requestCode, granted);
}
// Report denied permissions, if any.
if
(!denied.isEmpty()) {
callbacks.onPermissionsDenied(requestCode, denied);
}
// If 100% successful, call annotated methods
if
(!granted.isEmpty() && denied.isEmpty()) {
runAnnotatedMethods(object, requestCode);
}
}</string></string></code></code></code></code>
|
这个框架只有一个类,相对学习起来都比较容易,看完这个代码,自己对权限这块就会比较清晰了。
但是权限的这些API这不同的手机上的呈现都有不同。这个的兼容问题,就要靠大家的努力一起解决了。
比如我现在就遇到了,在小米上,同时请求手机及状态和通讯录通话记录读取的时候,用户点击了拒绝,但是权限检查权限的方法却返回了成功。
这个问题我还没解决,大家如果有好的办法,请在留言板回复。
这里我为大家提供一些便利。我将需要运行检查的呢些权限都写成常量放在一起了。也方便以后找那些权限。
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
|
<code><code><code><code>
/**
* Dangerous Permissions
* 坑爹的危险权限
* */
public
static
final
String WRITE_CONTACTS= Manifest.permission.WRITE_CONTACTS;
//写入联系人,但不可读取
public
static
final
String GET_ACCOUNTS= Manifest.permission.GET_ACCOUNTS;
//访问一个帐户列表在Accounts Service中
public
static
final
String READ_CONTACTS= Manifest.permission.READ_CONTACTS;
//读取联系人,但不可写入
public
static
final
String READ_CALL_LOG= Manifest.permission.READ_CALL_LOG;
//读取通话记录
public
static
final
String READ_PHONE_STATE= Manifest.permission.READ_PHONE_STATE;
//读取手机状态
public
static
final
String CALL_PHONE= Manifest.permission.CALL_PHONE;
//读取手机号码
public
static
final
String WRITE_CALL_LOG= Manifest.permission.WRITE_CALL_LOG;
//写入通话记录
public
static
final
String USE_SIP= Manifest.permission.USE_SIP;
public
static
final
String PROCESS_OUTGOING_CALLS= Manifest.permission.PROCESS_OUTGOING_CALLS;
//允许程序监视、修改有关播出电话
public
static
final
String ADD_VOICEMAIL= Manifest.permission.ADD_VOICEMAIL;
public
static
final
String READ_CALENDAR= Manifest.permission.READ_CALENDAR;
//允许程序读取用户日历数据
public
static
final
String WRITE_CALENDAR= Manifest.permission.WRITE_CALENDAR;
//允许一个程序写入但不读取用户日历数据
public
static
final
String CAMERA= Manifest.permission.CAMERA;
//相机
public
static
final
String BODY_SENSORS= Manifest.permission.BODY_SENSORS;
//人体传感器;
public
static
final
String ACCESS_FINE_LOCATION= Manifest.permission.ACCESS_FINE_LOCATION;
//这个权限用于访问GPS定位
public
static
final
String ACCESS_COARSE_LOCATION= Manifest.permission.ACCESS_COARSE_LOCATION;
//这个权限用于进行网络定位
public
static
final
String READ_EXTERNAL_STORAGE= Manifest.permission.READ_EXTERNAL_STORAGE;
//读取外部存储器
public
static
final
String WRITE_EXTERNAL_STORAGE= Manifest.permission.WRITE_EXTERNAL_STORAGE;
//写入外部存储器
public
static
final
String RECORD_AUDIO= Manifest.permission.RECORD_AUDIO;
//允许程序录制音频
public
static
final
String READ_SMS= Manifest.permission.READ_SMS;
//读取短信
public
static
final
String RECEIVE_WAP_PUSH= Manifest.permission.RECEIVE_WAP_PUSH;
//允许程序监控将收到WAP PUSH信息
public
static
final
String RECEIVE_MMS= Manifest.permission.RECEIVE_MMS;
//允许一个程序监控将收到MMS彩信,记录或处理
public
static
final
String RECEIVE_SMS= Manifest.permission.RECEIVE_SMS;
//允许程序监控一个将收到短信息,记录或处理
public
static
final
String SEND_SMS= Manifest.permission.SEND_SMS;
//允许程序发送SMS短信</code></code></code></code>
|
EasyPermiss项目地址:https://github.com/googlesamples/easypermissions