如今的手机操作系统已经到了8.0,早在6.0的时候就有了权限的动态申请,危险的权限在使用到的时候动态申请。比如相机权限、读写权限等。
需要动态申请的危险权限有
CONTACTSREAD_CONTACTS ,WRITE_CONTACTS, GET_ACCOUNTS
ACCESS_FINE_LOCATION , ACCESS_COARSE_LOCATION
MICROPHONERECORD_AUDIO
READ_PHONE_STATE, CALL_PHONE READ_CALL_LOG , WRITE_CALL_LOG , ADD_VOICEMAIL , USE_SIP, PROCESS_OUTGOING_CALLS
SEND_SMS RECEIVE_SMS , READ_SMS RECEIVE_WAP_PUSH , RECEIVE_MMS
READ_EXTERNAL_STORAGE , WRITE_EXTERNAL_STORAGE
这些权限在使用的时候需要现在AndroidManifest声明,然后在使用到的时候动态申请。接下来说一下我在使用中遇到的问题。
最近做的项目中用到了读写sd卡和相机的权限,之前做的都是把targetSdkVersion定在23以下,越过动态权限配置部分,这样总归不是个办法,所以在网上寻找动态权限配置的方法。找了一些也试了一些,但是在小米3上总归不是很完美。我的实现思路如下如下:
在程序启动的时候检查是否有对应的权限(我这里是读写和相机的权限),如果有权限则程序继续执行相应的代码,如果没有权限则提示询问用户是否授权,如果用户选择授权则继续执行相应的代码,如果用户选择了否且没有勾选不在提示则再次询问用户是否授权,知道用户授权才可登陆。如果用户选择了不授权并且勾选了不在提示,则提示用户去设置中开启对应的权限,如果用去开启并且开始了全部权限,用户再次登陆会直接接入到程序,如果选择了不开启则退出程序。
可是我在实现的手发现,第一次启动程序的时候提示用户授权没有问题,但是如果用户选择了否的话就不会弹出系统的提示框了,但是会走onRequestPermissionsResult()回调方法,根据shouldShowRequestPermissionRationale()方法来判断还是不行,最后实在没办法了就直接略掉了中的再次弹出提示框的部分,用户拒绝授权之后就直接弹出提示框让用户到设置中去开启对应的权限,只有用户把所有的权限都开启之后才可以登陆,否则每次启动都会提示。
下面贴出实现方法和简略效果图:
首先是公共类:
public classPermissionsUtil {
// 状态码、标志位
public static final intREQUEST_STATUS_CODE=0x001;
public static final intREQUEST_PERMISSION_SETTING=0x002;
//常量字符串数组,将需要申请的权限写进去,同时必须要在Androidmanifest.xml中声明。
public staticString[]PERMISSIONS_GROUP_SORT= {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
};
private staticPermissionCallbackscallbacks;
public interfacePermissionCallbacks {
voidonPermissionsGranted();//权限都有
voidonPermissionsDenied(intrequestCode, List perms);
}
public static voidcheckAndRequestPermissions(finalActivity activity, PermissionCallbacks callback) {
callbacks= callback;
if(Build.VERSION.SDK_INT>=23) {
// 一个list,用来存放没有被授权的权限
ArrayList denidArray =newArrayList<>();
// 遍历PERMISSIONS_GROUP,将没有被授权的权限存放进denidArray
for(String permission :PERMISSIONS_GROUP_SORT) {
intgrantCode = ActivityCompat.checkSelfPermission(activity, permission);
if(grantCode == PackageManager.PERMISSION_DENIED) {
denidArray.add(permission);
}
}
// 如果该字符串数组长度大于0,说明有未被授权的权限
if(denidArray.size() >0) {
//循环处理所有未授权的权限,每次只添加一个权限进行获取
ArrayList denidArrayNew =newArrayList<>();
denidArrayNew.add(denidArray.get(0));
// 将denidArray转化为字符串数组,方便下面调用requestPermissions来请求授权
String[] denidPermissions = denidArrayNew.toArray(newString[denidArrayNew.size()]);
requestPermissions(activity, denidPermissions);
}else{
//已授权
callbacks.onPermissionsGranted();
}
}else{
callbacks.onPermissionsGranted();
}
}
/**
* 关于shouldShowRequestPermissionRationale函数的一点儿注意事项:
* ***1).应用安装后第一次访问,则直接返回false;
* ***2).第一次请求权限时,用户Deny了,再次调用shouldShowRequestPermissionRationale(),则返回true;
* ***3).第二次请求权限时,用户Deny了,并选择了“dont ask me again”的选项时,再次调用shouldShowRequestPermissionRationale()时,返回false;
* ***4).设备的系统设置中,禁止了应用获取这个权限的授权,则调用shouldShowRequestPermissionRationale(),返回false。
*/
public static booleanshowRationaleUI(Activity activity, String permission) {
returnActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
}
/**
* 对权限字符串数组中的所有权限进行申请授权,如果用户选择了“dont ask me again”,则不会弹出系统的Permission申请授权对话框
*/
public static voidrequestPermissions(Activity activity, String[] permissions) {
ActivityCompat.requestPermissions(activity, permissions,REQUEST_STATUS_CODE);
}
/**
* 用来判断,App是否是首次启动:
* ***由于每次调用shouldShowRequestPermissionRationale得到的结果因情况而变,因此必须判断一下App是否首次启动,才能控制好出现Dialog和SnackBar的时机
*/
public static booleanisAppFirstRun(Activity activity) {
if(PreferencesUtils.getBoolean(activity,"first_run",true)) {
PreferencesUtils.putBoolean(activity,"first_run",false);
return true;
}else{
PreferencesUtils.putBoolean(activity,"first_run",false);
return false;
}
}
}
然后再启动页执行如下代码:
PermissionsUtil.checkAndRequestPermissions(this,object: PermissionsUtil.PermissionCallbacks{
override funonPermissionsGranted() {
dosomething()
}
override funonPermissionsDenied(requestCode: Int, perms: MutableList?) {
}
})
当然回调是必不可少的
override funonRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray){
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == PermissionsUtil.REQUEST_STATUS_CODE) {
if(permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {//读写权限
if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {//同意
PermissionsUtil.checkAndRequestPermissions(this,object:PermissionsUtil.PermissionCallbacks{
override funonPermissionsGranted() {
dosomething()
}
override funonPermissionsDenied(requestCode: Int, perms: MutableList?) {
}
});//请求
}else{//不同意
createLoadedAlertDialog("在设置-应用-"+ getString(R.string.app_name) +"-权限中开启存储空间权限,以正常使用App功能");
}
}
if(permissions[0].equals(Manifest.permission.CAMERA)) {//相机权限
if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {//同意
PermissionsUtil.checkAndRequestPermissions(this,object:PermissionsUtil.PermissionCallbacks{
override funonPermissionsGranted() {
dosomething()
}
override funonPermissionsDenied(requestCode: Int, perms: MutableList?) {
}
});
}else{//不同意
createLoadedAlertDialog("在设置-应用-"+ getString(R.string.app_name) +"-权限中开启电话权限,以正常使用App功能");
}
}
}
}
每个权限都单独做了处理,避免没有权限进入会崩溃。
下面是项目运行截图:
第一次登陆弹出系统提示框,询问用户是否授权
用户选择拒绝后弹出用户去设置的弹框,如果用户选择设置并且去开启了相应的权限则用户再次登陆进入程序,如果用户点击取消则退出程序,用户再次进入还会弹出弹框,逻辑与上面相同。
结语:由于没怎么写过类似的文章在逻辑上还有很多地方写的不是很好,请大家多多指点。有问么疑问提出来大家一起探讨。