我们知道从Android 6.0(API 23) 开始,Google在Android上引入了动态权限模式,即用户可在应用运行时管理权限, 这种模式让用户能够更好地了解和控制权限,用户可为所安装的各个应用分别授予或撤销权限。 但是对于开发者来说,为了适配Android 6.0(API 23)及以上版本,除了在AndroidManifest.xml中去声明相关权限, 对于Android官网中划分的危险权限 https://developer.android.com/guide/topics/permissions/overview, 还需要在代码中判断该应用是否被授予权限,以保证程序的正常运行。
(1)ContextCompat.checkSelfPermission()
ContextCompat.checkSelfPermission()方法来检测某个权限是否被授予
(2) ActivityCompat.requestPermissions()
ActivityCompat.requestPermissions()方法用来动态申请权限,我们在调用requestPermission()方法去申请权限时,系统会弹出权限申请提示框,可以同意授予,用户可以拒绝,如果用户选择了拒绝,并且勾选了不再提示,那么再次调用ActivityCompat.requestPermissions()方法申请该权限时,系统就不会弹出权限申请的弹窗了。
(3)onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
回调方法
无论用户同意授权,还是拒绝了授权,都会在onReuqestPermissionResult()回调方法中返回用户的选择结果。
(4)shouldShowRequestPermissionRationale()
前面说过,使用ActivityCompat.requestPermissions()方法用来动态申请权限,系统会弹出权限申请提示框,可以同意授予,用户可以拒绝,如果用户选择了拒绝,并且勾选了不再提示,那么再次调用ActivityCompat.requestPermissions()方法申请该权限时,系统就不会弹出权限申请的弹窗了,那么我们该怎么告知用户需要去开启某个权限呢?
这个时候就要引出我们今天说的主角shouldShowRequestPermissionRationale(),该方法来判断用户是否拒绝过授予权限,并且勾选了不再提示操作。如果shouldShowRequestPermissionRationale()返回true,则说明之前用户绝拒绝了授权,并且勾选了不再提示,这个时候应用可以提示用户去设置中开启权限。
介绍了上面的几个方法,下面给出之前我们项目中实现高德地图定位,动态申请权限的代码
高德地图定位需要动态申请的权限有:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
/**
* 初始化高德定位
*/
private void initGaoDeLocation() {
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { //用户已拒绝过一次
//提示用户如果想要正常使用,要手动去设置中授权。
ToastUtil.showShort(mActivity, "请到设置-应用管理中开启此应用的读写权限");
} else {
ActivityCompat.requestPermissions(mActivity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE},
PERMISSION_REQUEST_CODE_LOCATION);
}
} else if (ContextCompat.checkSelfPermission(mActivity, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity, Manifest.permission.ACCESS_FINE_LOCATION)) { //用户已拒绝过一次
//提示用户如果想要正常使用,要手动去设置中授权。
ToastUtil.showShort(mActivity, "请到设置-应用管理中开启此应用的定位权限");
} else {
ActivityCompat.requestPermissions(mActivity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE},
PERMISSION_REQUEST_CODE_LOCATION);
}
} else if (ContextCompat.checkSelfPermission(mActivity, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity, Manifest.permission.READ_PHONE_STATE)) { //用户已拒绝过一次
//提示用户如果想要正常使用,要手动去设置中授权。
ToastUtil.showShort(mActivity, "请到设置-应用管理中开启此应用的电话权限");
} else {
ActivityCompat.requestPermissions(mActivity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE},
PERMISSION_REQUEST_CODE_LOCATION);
}
} else {
DialogUtil.showUnCancelableProgress(mActivity, "正在进行定位");
gaoDeLocation();
}
} else {
DialogUtil.showUnCancelableProgress(mActivity, "正在进行定位");
gaoDeLocation();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSION_REQUEST_CODE_LOCATION:
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
ToastUtil.showShort(mActivity, "请在到设置-应用管理中开启此应用的储存授权");
return;
}
if (grantResults[1] != PackageManager.PERMISSION_GRANTED) {
ToastUtil.showShort(mActivity, "请在到设置-应用管理中开启此应用的定位权限");
return;
}
if (grantResults[2] != PackageManager.PERMISSION_GRANTED) {
ToastUtil.showShort(mActivity, "请在到设置-应用管理中开启此应用的获取电话权限");
return;
}
DialogUtil.showUnCancelableProgress(mActivity, "正在进行定位");
gaoDeLocation();
break;
}
}
但是我们今天的标题是Android 动态权限申请之 shouldShowRequestPermissionRationale方法异常,所以回到我们的主题,由于Android系统的碎片化,在国内,不同手机厂商都对Android系统都做了不同的定制,上面的代码在大部分手机上测试没有任何问题,直到某天在公司的测试机 努比亚 Z17 mini S,Android系统版本是Android 7.1.1,发现首次执行该段代码,shouldShowRequestPermissionRationale()就返回true,
也就是说在这个努比亚手机上,用户没有拒绝过授予权限,并且也没有勾选不再提示,shouldShowRequestPermissionRationale()也会返回true,所以上面的这段代码,永远也不会执行到ActivityCompat.requestPermissions(mActivity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE},
PERMISSION_REQUEST_CODE_LOCATION);
永远不会弹出权限授予弹窗。
所以为了适配努比亚手机,我将上述代码改成了如下:
/**
* 初始化高德定位
*/
private void initGaoDeLocation() {
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(mActivity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE},
PERMISSION_REQUEST_CODE_LOCATION);
} else if (ContextCompat.checkSelfPermission(mActivity, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(mActivity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE},
PERMISSION_REQUEST_CODE_LOCATION);
} else if (ContextCompat.checkSelfPermission(mActivity, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(mActivity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE},
PERMISSION_REQUEST_CODE_LOCATION);
} else {
DialogUtil.showUnCancelableProgress(mActivity, "正在进行定位");
gaoDeLocation();
}
} else {
DialogUtil.showUnCancelableProgress(mActivity, "正在进行定位");
gaoDeLocation();
}
}
不再使用shouldShowRequestPermissionRationale,只要判断到权限没有被授予,就去调用 ActivityCompat.requestPermissions()去申请权限;如果用户选择了拒绝,并且勾选了不再提示,调用ActivityCompat.requestPermissions(),虽然不会弹出权限申请弹窗,但是会直接
回调onRequestPermissionResult()方法,我们只需要在该回调方法中去提示用户去设置中开启权限即可。