一,前言
最近App的targetSdkVersion提升到23以上,因此需要处理动态权限的问题,因此网上学习了动态权限的一些知识,在实践中也遇到了不少的问题,因此在此做记录。
二,关于动态权限
安卓系统的权限管理机制从Android 6.0( Android M,对应的API 23)之后发生了很大的改变,为了对app安全运行有更好的控制,谷歌对权限进行了分类,权限分为普通权限(Normal Permissions)和危险权限(Dangerous Permissions),普通权限是一些不会涉及用户隐私,拥有权限也不会造成什么危险,比如访问网络等,如果需要在Mainfest.xml里添加即可,app会在安装的时候被给予权限,而且一旦给予权限也没有地方可以取消。而危险权限则是一些会涉及到用户隐私,需要让用户来决定是否接受赋予app的权限,比如摄像头,通讯录等等。被列为危险的权限要求必须申请动态权限,并且也需要在Mainfest.xml里添加。如果没有权限去运行则会崩溃。
普通权限不多做介绍了,可以去谷歌官网自行查找,下面对危险权限进行讲解,谷歌对危险权限提出了权限组的概念,即相似权限的在一个权限组里,赋予权限时是以权限组的方式赋予的,比如sd卡的读写权限组:
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
申请权限时会把权限组的权限一起赋予,比如申请sd卡写权限,同时会把读权限一起给予(8.0之后对此做了更为严格的限制,同一权限组也必须单独的申请,不过如果之前已经申请过该组里的权限,则不会再通知用户)。危险权限共分为九组,具体权限组如下:
申请动态权限发生的场景:
1,targetSdkVersion>=23
2,手机是6.0及以上的。
如果手机系统是6.0以下,或者targetSdkVersion<23,无论手机系统版本是不是6.0以上,都不会进 行动态权限的申请。(M以下的系统进行权限查询checkSelfPermission会直接返回true)。
三,动态权限申请步骤
1,在AndroidManifest文件中添加需要的权限。(如果没有声明,直接去申请权限会导致程序崩溃)。
2,检查权限:
int checkSelfPermission(@NonNull Context context, @NonNull String permission)
private boolean checkPermission(String permission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
return false;
}
} else {
return true;
}
}
3,申请授权
void ActivityCompat.requestPermissions(final @NonNull Activity activity,final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode)
private void startRequestPermision(String[] permissions) {
ActivityCompat.requestPermissions(MainActivity.this, permissions, REQUEST_CODE);
}
该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。
4,处理权限申请回调
void onRequestPermissionsResult(intrequestCode, String permissions[],int[] grantResults)
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//TODO
} else {
//如果拒绝授予权限,且勾选了再也不提醒
if (!shouldShowRequestPermissionRationale(permissions[0])) {
//说明申请权限原因 showDescDialog();
} else {
//去设置页面 goSetting();
}
}
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
申请的权限系统会通过弹窗逐一让用户选择是否给予,如果不给予而又必须申请权限,则有两种情况,如果用户没有勾选“不再提醒”选项,则还可以通过弹窗来说明申请权限的原因。如果用户勾选了“不再提醒”选项,则只能通过去设置页面让用户手动添加权限。
四,遇到的问题
1,不同手机厂商对权限申请机制做了修改
在申请权限时表现形式会有所不同,不过大部分的流程还是和原生系统大同小异。比如小米手机,小米手机在6.0以下已经增加了动态权限,是通过类似安全管家的软件进行权限检测。有一些厂商修改的动态权限申请有问题,比如魅族的flyme,在一个6.0的版本上,检查权限会告知有权限,因此检查步骤通过,但是实际上并没有给予,在实际使用权限的时候触发告知app正在请求使用权限,即使此时同意,也不能避免此次调用失败,再次使用时才成功。确实挺坑的,也没想到好的解决方案。
2,用户在应用设置页面手动操作权限对应用的影响
app如果在后台正在运行,当在系统设置里增加权限没问题,如果有减少权限的动作(1,减少某一个权限; 2,对某个权限增加又减少),则都会触发系统把app进程kill掉,然后重新启动进程。具体现象如下:
从后台中重新打开app时,activity的栈为空,系统自动把退到后台时显示的activity放到栈顶,并且记录了原来的栈顺序,可以像原来栈顺序一样后退,但是每个activity显示时都是重新创建(onCreate),另外重启后内存数据全部丢失了。针对该问题有如下的处理方法,在ActivityLifeCycle里增加记录Activity栈里activity数量的变量,可以检测到栈为空到有的时刻,如果该时刻第一个入栈的activity不是闪屏页(第一个启动的页面)则重启应用。
网友评论