关于Android的权限问题一直都是一知半解,而且最近有一个关于权限的需求,因此想着借这个机会自己整理一下相关的东西,让自己清楚了解。不知道最后会写成什么样子,哈哈。
安卓系统的权限管理机制从API 23 (也就是Android 6.0 又叫做 Android M,)之后发生了比较大的改变,在一些比较危险的权限上要求必须申请动态权限,即使你在AndroidMainfest.xml文件中申请也没有任何用,或者可以将编译的目标版本设定这API 22,这样就可以了。但这并不是长久之计。
Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。
在低于6.0的系统中,直接安装apk的时候在安装页面会有一个应用权限的直接申请,这里的权限是和manifest中添加的权限一致的。如果想修改可以在手机管家中进行修改。如下图
在手机版本高于6.0的系统中申请的权限会更加细致,同一个安装包,在10.0的系统上权限如下
10.0的系统安装完成以后在安装完成页还可以再对权限进行一次设置
当系统小于Android 6.0,只需要在AndroidManifest.xml注册权限即可使用(不包括某些手机厂商自定义的权限管理)。当系统大于等于Android 6.0时,既要在AndroidManifest.xml注册权限,又要去动态申请权限。
下面以摄像机权限为例写一下具体的代码实现以及页面上的效果。
1.6.0系统的手机上需要动态申请权限。
权限的申请分为3步:1.查看是否已获取权限;2.如果没有获取到权限,去申请权限;3.拿到申请权限结果。
而第一步中,在低于6.0的手机中查询需要的权限状态时一直是出于已有权限状态,即
不管在手机管理中将摄像头权限设置为允许还是禁止,上图中这行代码的结果一直是true。
另外,Android权限系统还提供了一种非常“恶心”的机制,叫拒绝并不再询问。
当某个权限被用户拒绝了一次,下次我们如果再申请这个权限的话,界面上会多出一个拒绝并不再询问的选项。只要用户选择了这一项,那么我们之后都不能再去请求这个权限了,因为系统会直接返回我们权限被拒绝。
点击拒绝和拒绝并不再询问选项都是拒绝,那么怎样区别他们呢?答案就是ActivityCompat.shouldShowRequestPermissionRationale方法。代码如下,
如果点击禁止按钮,ActivityCompat.shouldShowRequestPermissionRationale的结果为true,这时去弹一个框告诉用户这个功能需要开启权限,点击确定按钮后重新申请权限,引导用户同意权限。如果点击禁止并不再询问按钮,ActivityCompat.shouldShowRequestPermissionRationale的结果为false,这时也去弹一个框告诉用户这个功能需要开启权限,但是现在再去申请权限就不能再弹出申请权限的选项了,因此我们的做法是弹一个框告诉用户这个功能需要开启权限,在点击确认按钮后跳转到设置页去引导用户开启权限。
2.好了,现在一个整体上申请权限的例子就写完了。上面的是申请一个权限,那么在项目中我们也有可能要同时申请多个权限,那么该怎么做呢?难道要每个权限都写这么一串吗?那样显得代码太臃肿了,现在就来看看以两个权限为例改怎么做吧。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case 0: {
List denied =new ArrayList();//存放拒绝的权限
List deniedAndNotAllow =new ArrayList();//存放拒绝且不再询问的权限
for (int i =0; i < grantResults.length; i++) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), permissions[i])) {
//点击“禁止”
denied.add(permissions[i]);
}else {
//点击“拒绝并不再询问”
deniedAndNotAllow.add(permissions[i]);
}
}
}
if (denied.size() ==0 && deniedAndNotAllow.size() ==0) {
Intent intent =new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, REQUEST_CAMERA_CODE);
}else {
if (denied.size() !=0) {
new AlertDialog.Builder(getActivity()).setMessage("该功能需要您开启摄像头权限")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION}, 0);
}
}
).show();
}else {
new AlertDialog.Builder(getActivity()).setMessage("该功能需要您开启摄像头权限,请去设置中统一摄像头权限")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//打开app的设置页面,引导用户开启权限
Intent intent =new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
}
).show();
}
}
}
return;
}
}
上面代码粘贴过来格式有点乱了,整体流程就是在onRequestPermissionsResult()方法中增加了denied和deniedAndNeverAskAgain两个集合,分别用于记录拒绝和拒绝并不再询问的权限。如果这两个集合都为空,那么说明所有权限都被授权了,这时就可以直接进行拍照了。
而如果denied集合不为空,则说明有权限被用户拒绝了,这时候我们还是弹出一个对话框来提醒用户,并重新申请权限。而如果deniedAndNeverAskAgain不为空,说明有权限被用户拒绝且不再询问,这时就只能提示用户去设置当中手动打开权限,我们编写了一个Intent来执行跳转逻辑。
在书写的过程中我认为有两点需要注意:1. 这里拍照功能是需要拍照权限和位置权限同时满足,在真是的项目中可能有变动,大家根据实际情况进行书写即可。2. 两个权限一起申请的时候我使用了两个手机,一个是8.0系统,另一个是10.0系统,在10.0系统手机上连续弹出拍照权限和位置权限的选择,而在8.0的系统上只弹出了位置权限的获取申请,猜想是第二个权限申请把前一个盖掉了。这是两个系统版本的不同点。