android 6.0动态权限申请工具类,【需求解决系列之五】一行代码实现Android 6.0以上动态权限的申请...

前言

自从Android6.0以后,运行时权限申请就成为了一大痛点!动态申请吧,说它难吧,它又不难,说它不难吧,申请起来贼复杂;不申请吧,还要给你整闪退。

其实现在有很多权限申请框架了,那么为什么我还在造轮子呢?其实原因很简单,可能是年纪大了,开始喜欢简约风了,能少写一行的我绝对不想多写一个字母!说到底还是觉得现有的很多框架,用起来不是那么顺手,配置相对复杂,所以萌生了再整一个一行代码实现权限申请的想法。

系列

在工作之余,打算将一些常用的逻辑页面,模块,功能点做成library库,这样当有相似需求的时候,可以做到插拔式开发!现在系列中有以下内容

Github地址

具体的实现demo已经放到Github了,效果图什么的也在上面,上面也有写好的demo apk提供下载尝试,如果你觉得有用,记得star哦!哈哈哈,就是这么不要脸~~~

正文

实现思路

权限申请无非三板斧,第一:检查是否已授权,第二:发起授权申请,第三:处理授权结果!第一步和第二步是固定的操作,只不过我们可以将繁琐的步骤封装起来。

第三步才是不好处理的核心,正常来说,当我们发起权限申请之后,必须要重写onRequestPermissionsResult方法来对授权结果进行处理,这就导致了无论你怎么封装,还是必须侵入到每个Activity的onRequestPermissionsResult中去,这就意味着不管怎样,你都至少要在两个地方写上对应的代码才能实现授权功能。但是作为调用者来说,我希望的操作只有两个,一,提供我需要授权的权限,二,回调给我授权结果。

如何实现通过回调的方式反馈给调用者授权结果呢?最开始想法是写一个透明宽高各1像素大小的Activity,将权限申请和授权结果的操作都放在里面处理,处理完之后关闭此Activity,对于用户来说其实也是无感知的。但是有一个最大的问题就是,当在这个Activity处理完onRequestPermissionsResult之后,如何才能通过回调的方式告知调用方呢?由于打开新的Activity之后,调用方Activity和处理逻辑的Activity是两个不同的页面,在不借助其他技术的情况下,是不好实现类似接口回调的方式进行通讯的,所以摒弃了这个方案。

后来想到,既然Activity不能胜任这份工作,可以尝试一下它的兄弟Fragment,Fragment中也可以发起授权申请并处理授权结果,最重要的是,Fragment是依附于Activity的,所以我们可以在同一个Activity下进行权限的申请和处理,并且不会污染到宿主Activity的逻辑,美滋滋。

实现过程

配置并申请权限

对于调用者来说,他需要做的就是配置他需要申请的权限列表。所以我们需要提供一个入口给他。

FanPermissionUtils.with(MainActivity.this)

//添加所有你需要申请的权限

.addPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)

.addPermissions(Manifest.permission.ACCESS_FINE_LOCATION)

.addPermissions(Manifest.permission.CALL_PHONE)

.addPermissions(Manifest.permission.ACCESS_WIFI_STATE)

.addPermissions(Manifest.permission.CAMERA)

...

我们通过FanPermissionUtils.with方法构造出一个FanPermissionUtils工具类,使用链式方法让用户配置他需要申请的所有权限。FanPermissionUtils的内部维护了权限列表,存储所有需要授权的权限。

调用者配置好权限之后,调用startCheckPermission开始进行权限申请。

FanPermissionUtils.with(MainActivity.this)

//添加所有你需要申请的权限

.addPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)

.addPermissions(Manifest.permission.ACCESS_FINE_LOCATION)

.addPermissions(Manifest.permission.CALL_PHONE)

.addPermissions(Manifest.permission.ACCESS_WIFI_STATE)

.addPermissions(Manifest.permission.CAMERA)

//开始授权

.startCheckPermission();

此时,我们会构造一个没有页面的FanPermissionFragment来处理授权相关的逻辑,并将构造出的Fragment添加到宿主Activity中。

//构造FanPermissionFragment并将自己添加到宿主Activity中

FanPermissionFragment.newInstance(permissions).start(mContext);

/**

* 开始请求

*/

public void start(Activity activity) {

if (activity != null) {

mContext = activity;

if (Looper.getMainLooper() != Looper.myLooper()) {

return;

}

activity.getFragmentManager().beginTransaction().add(this, activity.getClass().getName()).commit();

}

}

一旦FanPermissionFragment被构造,就会对调用方提供的权限列表进行逐一排查,罗列出还未授权的权限列表,并对这些未授权的权限发起二次权限申请。

//记录未授权的权限

List deniedPermissions = new ArrayList<>();

for (String permission : permissions) {

int check = ContextCompat.checkSelfPermission(getActivity(), permission);

if (check == PackageManager.PERMISSION_GRANTED) {

//授权通过了已经 do nothing

} else {

deniedPermissions.add(permission);

}

}

if (deniedPermissions.size() != 0) {

//有权限没有通过

requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), PERMISSION_REQUEST_CODE);

} else {

//授权全部通过

}

这样我们就完成了第一步和第二步。

对授权结果进行处理

对于调用者来说,最佳的体验就是回调的方式反馈授权结果。先定义一个回调接口。

public interface FanPermissionListener {

/*

* 授权全部通过

*/

void permissionRequestSuccess();

/*

* 授权未通过

* @param grantedPermissions 已通过的权限

* @param deniedPermissions 拒绝的权限

* @param forceDeniedPermissions 永久拒绝的权限(也就是用户点击了不再提醒的那些权限)

*/

void permissionRequestFail(String[] grantedPermissions, String[] deniedPermissions, String[] forceDeniedPermissions);

}

调用者在构造FanPermissionUtils的时候,设置回调监听即可。

FanPermissionUtils.with(MainActivity.this)

//添加权限申请回调监听 如果申请失败 会返回已申请成功的权限列表,用户拒绝的权限列表和用户点击了不再提醒的永久拒绝的权限列表

.setPermissionsCheckListener(new FanPermissionListener() {

@Override

public void permissionRequestSuccess() {

//所有权限授权成功才会回调这里

}

@Override

public void permissionRequestFail(String[] grantedPermissions, String[] deniedPermissions, String[] forceDeniedPermissions) {

//当有权限没有被授权就会回调这里

}

});

我们在FanPermissionUtils中会保存调用者设置的回调接口,然后在FanPermissionFragment被构造的时候将这个回调接口传给FanPermissionFragment。

FanPermissionFragment.newInstance(permissions).setPermissionCheckListener(listener).start(mContext);

然后在FanPermissionFragment中去处理申请授权之后的授权回调操作。将授权回调的结果通过调用者设置的回调监听返回给调用者。来实现侵入性最小的权限申请方案。

@Override

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

super.onRequestPermissionsResult(requestCode, permissions, grantResults);

if (requestCode == PERMISSION_REQUEST_CODE) {

//记录点击了不再提醒的未授权权限

List forceDeniedPermissions = new ArrayList<>();

//记录点击了普通的未授权权限

List normalDeniedPermissions = new ArrayList<>();

List grantedPermissions = new ArrayList<>();

for (int i = 0; i < grantResults.length; i++) {

int grantResult = grantResults[i];

String permission = permissions[i];

if (grantResult == PackageManager.PERMISSION_GRANTED) {

//授权通过

grantedPermissions.add(permission);

} else {

//授权拒绝

if (!ActivityCompat.shouldShowRequestPermissionRationale(mContext, permission)) {

//用户勾选了不再提示

forceDeniedPermissions.add(permission);

} else {

//用户仅仅点击了拒绝 未勾选不再提示

normalDeniedPermissions.add(permission);

}

}

}

if (forceDeniedPermissions.size() == 0 && normalDeniedPermissions.size() == 0) {

//全部授权通过

requestPermissionsSuccess();

} else {

//授权未通过

for (String permission : this.permissions) {

if (grantedPermissions.contains(permission)

|| normalDeniedPermissions.contains(permission)

|| forceDeniedPermissions.contains(permission)) {

} else {

//如果三者都不包含他 包名这个权限不是隐私权限 直接给就完事了 所以要放到已授权的权限列表里面去

grantedPermissions.add(permission);

}

}

requestPermissionsFail(grantedPermissions.toArray(new String[grantedPermissions.size()]),

normalDeniedPermissions.toArray(new String[normalDeniedPermissions.size()]),

forceDeniedPermissions.toArray(new String[forceDeniedPermissions.size()]));

}

}

}

//授权全部通过

private void requestPermissionsSuccess() {

if (permissionCheckListener != null) {

permissionCheckListener.permissionRequestSuccess();

}

mContext.getFragmentManager().beginTransaction().remove(this).commit();

}

//授权未通过

private void requestPermissionsFail(String[] grantedPermissions, String[] deniedPermissions, String[] forceDeniedPermissions) {

if (permissionCheckListener != null) {

permissionCheckListener.permissionRequestFail(grantedPermissions, deniedPermissions, forceDeniedPermissions);

}

mContext.getFragmentManager().beginTransaction().remove(this).commit();

}

畅想优化

此外还有一种需求,当用户不授权,就不给用。这种情况下我们需要在申请权限的时候,当用户点击拒绝的时候,一直无限申请权限,直到用户点击允许为止,对,就是这么流氓!

细心的同学在上面的代码中已经看到了一些逻辑,对应的就是另外一种一种情况,用户在你连续弹出几次申请权限的弹窗之后,会选择勾选不再提醒,这个时候你再去申请权限的时候,系统会直接回调给你拒绝,并且不会弹出授权弹窗,这个时候我们需要调用ActivityCompat.shouldShowRequestPermissionRationale(mContext, permission)方法来判断下用户是否勾选了不再提醒的按钮,如果用户勾选了,那么我们就需要指引用户去设置页面手动授权,因为我们在app内部已经无能为力了。

if (normalDeniedPermissions.size() != 0) {

//还有普通拒绝的权限可以弹窗

requestPermission();

} else {

//所有没有通过的权限都是用户点击了不再提示的 我擦 这里本来是想把未授权的所有权限的名称列出来展示的 后来想想觉得配置有点麻烦

new AlertDialog.Builder(mContext)

.setTitle(mContext.getString(R.string.permissions_check_warn))

.setMessage(checkConfig == null ? forceDeniedPermissionTips : checkConfig.getForceDeniedPermissionTips())

.setCancelable(false)

.setPositiveButton(mContext.getString(R.string.permissions_check_ok), new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

openSettingPage();

}

}).show();

}

牛逼回吹

之前不是说一行代码实现吗?下面就是整个申请权限的功能,的确只有一个结尾的分号,我说一行不算吹牛吧!~~~

FanPermissionUtils.with(MainActivity.this)

//添加所有你需要申请的权限

.addPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)

.addPermissions(Manifest.permission.ACCESS_FINE_LOCATION)

.addPermissions(Manifest.permission.CALL_PHONE)

.addPermissions(Manifest.permission.ACCESS_WIFI_STATE)

.addPermissions(Manifest.permission.CAMERA)

//添加权限申请回调监听 如果会返回已申请成功的权限列表,用户拒绝的权限列表和用户点击了不再提醒的永久列表

.setPermissionsCheckListener(new FanPermissionListener() {

@Override

public void permissionRequestSuccess() {

//所有权限授权成功才会回调这里

((TextVfindViewById(R.id.tv_result)).setText("授权结果\n\n所有权限都授权成;

Toast.makeText(MainActivity.this, "所有权限都授权Toast.LENGTH_SHORT).show();

@Override

public void permissionRequestFail(String[] grantedPermissions, StrideniedPermissions, String[] forceDeniedPermissions) {

//当有权限没有被授权就会回调这里

StringBuilder result = new StringBuilder("授权结果\n\n授权失败\n\n");

result.append("授权通过的权限:\n");

for (String grantedPermission : grantedPermissions) {

result.append(grantedPermission + "\n");

}

result.append("\n临时拒绝的权限:\n");

for (String deniedPermission : deniedPermissions) {

result.append(deniedPermission + "\n");

}

result.append("\n永久拒绝的权限:\n");

for (String forceDeniedPermission : forceDeniedPermissions) {

result.append(forceDeniedPermission + "\n");

}

((TextView) findViewById(R.id.tv_result)).setText(result);

Toast.makeText(MainActivity.this, "授权失败", Toast.LENGTH_SHORT).show();

}

})

//开始授权

.startCheckPermission();

结语

我是Cretin,一个可爱的小男孩。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值