https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous%29
Android 6.0(API 23)之后,加强了权限管理,现整理一下相关变化,如下。
给相关的权限声明一个逻辑上的分组名称;
同一分组的中成员会一起展现在用户的界面中;
同一组的任何一个权限被授权了,其他权限也自动被授权;
例如,一旦READ_EXTERNAL_STORAGE 被授权了,则App也有WRITE_EXTERNAL_STORAGE权限。
只要申请了就可以使用(在AndroidManifest.xml中添加标签),安装时不需要用户确认;
安装时需要用户的确认才可使用;
查看高风险权限可以使用如下adb 命令:
adb shell pm list permissions -d -g
结果如下图:
Dangerous Permissions:
group:com.google.android.gms.permission.CAR_INFORMATION
permission:com.google.android.gms.permission.CAR_VENDOR_EXTENSION
permission:com.google.android.gms.permission.CAR_MILEAGE
permission:com.google.android.gms.permission.CAR_FUEL
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.ANSWER_PHONE_CALLS
permission:android.permission.READ_PHONE_NUMBERS
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:com.google.android.gms.permission.CAR_SPEED
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
ungrouped:
permission:com.google.android.providers.talk.permission.WRITE_ONLY
permission:com.google.android.providers.talk.permission.READ_ONLY
注:上述这些权限是需要在运行时申请的。
只有当申请权限的应用程序的数字签名与声明此权限的应用程序的数字签名相同时(如果是申请系统权限,则需要与系统签名相同),才能将权限授给它;
在AndroidManifest.xml文件里申请,App安装时会默认获得这些权限;
即使是在Android6.0系统的手机上,用户也无法在安装后取消这些Normal权限;
和以前的处理是一样的,不变;
也需要在AndroidManifest.xml文件里申请,但是App安装时具体如果执行授权分以下几种情况:
1、targetSDKVersion < 23 & API(手机系统) < 6.0
安装时默认获得权限,且用户无法在安装App之后取消权限;
2、targetSDKVersion < 23 & API(手机系统) >= 6.0
安装时默认获得权限,但是用户可以在安装App完成后动态取消授权( 取消时手机会弹出提醒,告诉用户这个是为旧版手机打造的应用,让用户谨慎操作 )
3、targetSDKVersion >= 23 & API(手机系统) < 6.0
安装时默认获得权限,且用户无法在安装App之后取消权限;
4、targetSDKVersion >= 23 & API(手机系统) >= 6.0
安装时不会获得权限,可以在运行时向用户申请权限。用户授权以后仍然可以在设置界面中取消授权,用户主动在设置界面取消后,在app运行过程中可能会出现crash
EasyPermission库是一个谷歌官方提供的简化基本的系统权限逻辑的库,可用于在Android M或者更高版本上;
项目官网地址如下:https://github.com/googlesamples/easypermissions
dependencies {
// For developers using AndroidX in their applications
implementation 'pub.devrel:easypermissions:3.0.0'
// For developers using the Android Support Library
implementation 'pub.devrel:easypermissions:2.0.1'
}
权限可以是单个,也可以是一些列;
在EasyPermission库中,使用hasPermissions() 检查若干权限;
String[] PERMS = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
if (!EasyPermissions.hasPermissions(BaseActivity.this, PERMS)) {
// 没有权限,进行权限请求
}
第一个参数 : Context参数;
第二个参数 : 一些系列的权限,可以是单个,也可以是一组;
检查权限后,发觉用户没有赋予权限,这时候需要代码请求权限,让用户同意;
在EasyPermission库中,使用requestPermissions(),来请求权限;
String[] PERMS = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
final int PERMS_CODE = 55;
if (!EasyPermissions.hasPermissions(BaseActivity.this, PERMS)) {
// 没有权限,进行权限请求
EasyPermissions.requestPermissions(this, "为了正常使用App,请为App授权", PERMS_CODE, PERMS);
}
第一个参数:Context对象;
第二个参数:权限弹窗上的文字提示语,告诉用户,这个权限用途;
第三个参数:这次请求权限的唯一标示;
第四个参数 : 一些系列的权限;
请求后,弹出系统权限弹窗,剩下是用户是否授权操作;
权限结果是回调在Activity或者Fragment中的重写的onRequestPermissionsResult()方法中;
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 将结果转发给 EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
/**
* 请求权限成功。
* 可以弹窗显示结果,也可执行具体需要的逻辑操作
*
* @param requestCode
* @param perms
*/
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
ToastUtils.showMsg(this, "用户授权成功");
}
/**
* 请求权限失败
*
* @param requestCode
* @param perms
*/
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
ToastUtils.showMsg(this, "用户授权失败");
//若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不在提示',且拒绝权限。
//这时候,需要跳转到设置界面去,让用户手动开启。
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
new AppSettingsDialog.Builder(this).build().show();
}
}
将权限结果传递给EasyPermissions.onRequestPermissionsResult()来处理;
EasyPermissions.onRequestPermissionsResult()方法:
第一个参数: 请求的code
第二个参数: 一些列的请求权限
第三个参数: 用户授权的结果
第四个参数: 权限回调监听器
这里不需要手写判断权限是否成功的逻辑操作,而会在EasyPermissions.PermissionCallbacks监听器中响应;
EasyPermissions.PermissionCallbacks接口:
①onPermissionsGranted():用户授权成功,接下来执行具体需要的逻辑操作;
②onPermissionsDenied():用户授权失败,处理失败的逻辑;
在权限弹窗中,用户可能直接拒绝权限,下次权限请求依旧会弹出该权限弹窗。除此之外,还可以勾选’NEVER ASK AGAIN.’或者’不再提示’,且拒绝权限,下次请求权限,弹窗不能弹出,无法让用户授权。这时候,需要跳转到设置界面去,让用户手动开启。
在EasyPermission库中,使用EasyPermissions.somePermissionPermanentlyDenied()来处理,是否勾选不再提示的选项。
/**
* 请求权限失败
*
* @param requestCode
* @param perms
*/
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
ToastUtils.showMsg(this, "用户授权失败");
//若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不在提示',且拒绝权限。
//这时候,需要跳转到设置界面去,让用户手动开启。
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
new AppSettingsDialog.Builder(this).build().show();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
//当从软件设置界面,返回当前程序时候
case AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE:
//执行Toast显示或者其他逻辑处理操作
ToastUtils.showMsg(this, "从设置界面返回了!");
break;
}
}
当无法弹出权限弹框,直接跳转到设置界面去,让用户手动开启权限;
当从设置界面返回时候,结果会在Activity或者Fragment中onActivityResult()响应;