一、引言
动态权限管理是 Android 6.0(Build.VERSION_CODES.M = Api23)推出的,提醒用户当前 APP 所需要的权限,防止滥用。这些权限一般分为两种:
- 普通权限:直接manifest清单文件中写上注册就行了
- 危险权限:需要动态申请
这里我们按照原生、GitHub 上比较出名的第三方权限管理工具 RxPermissions 及 EasyPermissions 为例说明权限的应用。
二、权限说明
在 Android 6.0 如果不对关键权限作动态申请,则运行时就会出现异常:
FATAL EXCEPTION: main
AndroidRuntime: Process: com.cchip.smartwelding.weld, PID: 3496
AndroidRuntime: java.lang.RuntimeException: Unable to resume activity {com.cchip.smartwelding.weld/com.cchip.smartwelding.btcontrol.activities.MainActivity}: java.lang.SecurityException: com.cchip.smartwelding.weld was not granted this permission: android.permission.WRITE_SETTINGS.
流程:
- 第一次询问权限 -》允许权限(再次询问同样权限也不会弹框,已注册)
- 第一次询问权限-》拒绝权限-》再次询问权限-》允许权限(再次询问同样权限也不会弹框,已注册)
- 第一次询问权限-》拒绝权限-》再次询问权限-》不再询问(再次询问同样权限也不会有弹框,直接拒绝)
三、原生权限申请
原生权限比较长且繁琐,但相对也好理解。
private void initPermission() {
////判断当前系统是否高于或等于6.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS};
List<String> denyPermissions = new ArrayList<>();
for (String permission : permissions) {
//已注册权限
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, permission)) {
denyPermissions.add(permission);
//进入到这里代表没有权限.
}
}
if (!denyPermissions.isEmpty()) {
//未注册权限,申请权限
requestPermissions(denyPermissions.toArray(new String[denyPermissions.size()]), 101);
}
}
}
四、RxPermissions 权限管理
GitHub: RxPermissions
RxPermissions 是帮助开发者简化 requestPermissions() 相关的处理。使用起来觉得很简洁,最方便在 activity 的 onCreate() 中使用。
4.1 依赖配置
在 APP 的 build.gradle 中依赖 rxjava,rxandroid 和 rxpermissions
implementation 'io.reactivex.rxjava2:rxjava:2.1.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar' //最新为 V0.10.2
4.2 初始化
在 onCreate 中初化
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ButterKnife.bind(this);
getPermissions();
......
}
private void getPermissions() {
RxPermissions rxPermissions = new RxPermissions(this);
rxPermissions.requestEach(Manifest.permission.RECORD_AUDIO,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_CONTACTS,
Manifest.permission.CALL_PHONE,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.READ_EXTERNAL_STORAGE)
.subscribe(new Consumer<Permission>() {
boolean isDeniy = false;
@Override
public void accept(Permission permission) throws Exception {
if (permission.granted) {
// 用户已经同意该权限
if (permission.name.equals(Manifest.permission.READ_CONTACTS)) {
ContactUtils.getInstance().SearchAllContact();
} else if (permission.name.equals(Manifest.permission.ACCESS_COARSE_LOCATION)
|| permission.name.equals(Manifest.permission.ACCESS_FINE_LOCATION)) {
logShow("ACCESS_FINE_LOCATION");
GaodeUtils.getInstance().startLocation();
}
} else if (permission.shouldShowRequestPermissionRationale) {
// 用户拒绝了该权限,没有选中『不再询问』(Never ask again),那么下次再次启动时,还会提示请求权限的对话框
isDeniy = true;
} else {
isDeniy = true;
}
if (isDeniy && MainActivity.this.firstToast) {
// 用户拒绝了该权限,并且选中『不再询问』
MainActivity.this.firstToast = false;
Toast.makeText(MainActivity.this, getString(R.string.permissions_deniy), Toast.LENGTH_LONG).show();
}
}
});
}
五、EasyPermissions 权限管理
GitHub: EasyPermissions
EasyPermissions 框架是 Google 提供的,是一个简化基本的系统权限逻辑的库。
5.1 依赖配置
在 APP 的 build.gradle 中依赖 easypermissions
implementation 'pub.devrel:easypermissions:1.1.2' //最新为V2.0.0
5.2 重写 onRequestPermissionsResult
开始使用 EasyPermissions, 必须让你的 Activity 或者 Fragment 重写 onRequestPermissionsResult 方法:
public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// Forward results to EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsGranted(int requestCode, List<String> list) {
// Some permissions have been granted
// ...
}
@Override
public void onPermissionsDenied(int requestCode, List<String> list) {
// Some permissions have been denied
/**
* 若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不在提示',且拒绝权限。
* 这时候,需要跳转到设置界面去,让用户手动开启。
*/
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
new AppSettingsDialog.Builder(this).build().show();
}
}
}
5.3 请求权限 getPermission()方法
public void getPermission() {
//perms内填写需要动态申请的权限Manifest.permission.ACCESS_COARSE_LOCATION为作者随意写的(根据需要)
String[] perms = {Manifest.permission.ACCESS_COARSE_LOCATION};
//动态权限
if (EasyPermissions.hasPermissions(this, perms)) {
//有对应权限的操作
} else {
//没有对应权限的操作,"XXX权限"即为申请的权限名称
EasyPermissions.requestPermissions(getActivity, "XXX权限",QX_XX, perms);
}
}
六、总结
随着 Google 加强安全管理,及各大平台要求 TargetSdkVersion >= 26,在 APP 开发中,动态权限申请是不得不考虑的问题,否则一运行到就 Crash。