Android M 封装过的运行时权限处理

1,277 阅读4分钟

本文对Android M的运行时权限进行了代码封装,有助于项目开发业务代码中大面积的出现重复的运行时权限处理代码

1 前言

Android M 运行时权限想必大家已经不陌生了。在这还是放出一篇经典的对于运行时权限的说明和解释的说明。

Android M 新的运行时权限开发者需要知道的一切

同时Google也提供了帮助处理的第三方库EasyPermission,可以见这篇文章。

Android开源项目-Easypermissions

github.com/tsy12321/ea…
这个项目从Easypermissions中fork出来,翻译了中文文档,并封装了权限处理代码,使可以简化每次申请权限的流程。

2 出现的问题

所有的封装和处理都是由开发中出现的各种问题而推动的。很明显,权限处理的代码缺点就是你不能处理一次就行了。由于用户可以在系统设置-应用设置里手动设置权限,如下图。

权限修改
权限修改

所以需要在每次遇到Dangerous Permission 相关的业务处理时都要添加权限判断处理。虽然代码不多,但是需要同时在onRequestPermissionsResult、onPermissionsGranted、onPermissionsDenied等多处进行代码处理。就会显得很乱,降低代码的可读性。

最理想的方式当然时只要在需要的地方加一行代码即可以处理权限。封装权限处理应运而生。

3 封装后的处理

我们先直接看封装后的权限处理代码变成了什么样。下面是一个打开摄像头的业务,调用业务前需要处理摄像头权限。

public void cameraTask() {
    String[] perms = {Manifest.permission.CAMERA};
    performCodeWithPermission(getString(R.string.rationale_camera), RC_CAMERA_PERM, perms, new PermissionCallback() {
        @Override
        public void hasPermission(List<String> allPerms) {
            Toast.makeText(SimplePermActivity.this, "TODO: Camera things", Toast.LENGTH_LONG).show();
        }

        @Override
        public void noPermission(List<String> deniedPerms, List<String> grantedPerms, Boolean hasPermanentlyDenied) {
            if (hasPermanentlyDenied) {
                alertAppSetPermission(getString(R.string.rationale_ask_again), RC_SETTINGS_SCREEN);
            }
        }
    });
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == RC_SETTINGS_SCREEN) {
        // Do something after user returned from app settings screen, like showing a Toast.
        Toast.makeText(this, R.string.returned_from_app_settings_to_activity, Toast.LENGTH_SHORT)
                .show();
    }
}

注意下上面代码的alertAppSetPermission方法作用是,当权限被拒绝时,判断如果点了不再询问被拒绝的情况后弹出一个alert框,提示用户前往系统设置开启权限,这样更加人性化,不然如果用户拒绝过一次后之后的该权限将全部都会被拒绝。如下图

系统提示框
系统提示框

再看下alertAppSetPermission方法有2个重载方法,有一个比另一个多了个requestCode参数。目的是当用户跳转系统设置返回后,有的需求可能希望返回后自动再判断用户有没有在系统设置中修改过权限从而直接继续下一步操作。这样实现onActivityResult方法,在系统页面返回时再次判断权限是否授予。

通过上面代码可以发现,封装过后只需要在需要权限处理的地方加一个performCodeWithPermission,然后再重载onActivityResult方法。如果没有页面返回后自动继续下一步的需求的话那就只需要调用performCodeWithPermission就可以了。做到了“伪一行代码”处理运行时权限。哈哈!

没有对比就没有伤害,我再把没有封装过的完整权限处理代码贴上来对比下。(使用了EasyPermission)

@AfterPermissionGranted(RC_CAMERA_PERM)
public void cameraTask() {
    if (EasyPermissions.hasPermissions(this, Manifest.permission.CAMERA)) {
        // Have permission, do the thing!
        Toast.makeText(this, "TODO: Camera things", Toast.LENGTH_LONG).show();
    } else {
        // Ask for one permission
        EasyPermissions.requestPermissions(this, getString(R.string.rationale_camera),
                RC_CAMERA_PERM, Manifest.permission.CAMERA);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    // EasyPermissions handles the request result.
    EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}

@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
    Log.d(TAG, "onPermissionsGranted:" + requestCode + ":" + perms.size());
}

@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
    Log.d(TAG, "onPermissionsDenied:" + requestCode + ":" + perms.size());

    // (Optional) Check whether the user denied any permissions and checked "NEVER ASK AGAIN."
    // This will display a dialog directing them to enable the permission in app settings.
    if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
        new AppSettingsDialog.Builder(this, getString(R.string.rationale_ask_again))
                .setTitle(getString(R.string.title_settings_dialog))
                .setPositiveButton(getString(R.string.setting))
                .setNegativeButton(getString(R.string.cancel), null /* click listener */)
                .setRequestCode(RC_SETTINGS_SCREEN)
                .build()
                .show();
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == RC_SETTINGS_SCREEN) {
        // Do something after user returned from app settings screen, like showing a Toast.
        Toast.makeText(this, R.string.returned_from_app_settings_to_activity, Toast.LENGTH_SHORT)
                .show();
    }
}

发现比封装过的多了onRequestPermissionsResult、onPermissionsGranted、onPermissionsDenied三个回调的处理。就是说每次写权限处理需要写4~5个地方。而封装过的只需要写1~2个即可。

4 总结

所有的代码被封装在了BaseActivity中。其实很简单,不作过多解释了。大家可以到Github上自行参考。

所有的代码见Github:github.com/tsy12321/ea…

在项目开发中,多处需要进行权限处理,必要的封装可以大大增加代码可读性,专注于业务代码的编写。

更多文章关注我的公众号

我的公众号
我的公众号