PermissionsDispatcher:权限申请注解
相信Android开发的小伙伴一定有相同的感受,权限申请回调处理代码写起来很繁琐。尽管很多项目中都会将权限申请方法抽取成单独的工具,详见Android Permission 权限总结。
1、介绍
Android 6.0开始部分权限需要动态申请,相对于开发者来说代码又增加了很多重复的。2015年8月16号一个基于注解、帮助开发者简单处理运行时权限的开源库PermissionsDispatcher诞生了,避免开发者编写大量繁琐的样板代码。还有一个与此开源库配合使用的插件,详见PermissionsDispatcher插件。
GitHub:permissions-dispatcher/PermissionsDispatcher
2、添加依赖
在build.gradle中添加PermissionsDispatcher依赖,目前最新的版本是4.6.0
//PermissionsDispatcher权限
implementation "org.permissionsdispatcher:permissionsdispatcher:4.6.0"
annotationProcessor "org.permissionsdispatcher:permissionsdispatcher-processor:4.6.0"
3、部分源码
PermissionsDispatcher库极其简洁,通过注解实现权限管理。关于注解以后再深入的学习。
3.1、五个注解
在permissionsdispatcher-processor注解处理器中定义了五种注解,它们的用法后面详细介绍:
Annotation | Required | Description |
---|
@RuntimePermissions | ✓ | 注解在其内部需要使用运行时权限的Activity或Fragment上 |
@NeedsPermission | ✓ | 注解在需要调用运行时权限的方法上,当用户给予权限时会执行该方法 |
@OnShowRationale | | 注解在用于向用户解释为什么需要调用该权限的方法上,只有当第一次请求权限被用户拒绝,下次请求权限之前会调用 |
@OnPermissionDenied | | 注解在当用户拒绝了权限请求时需要调用的方法上 |
@OnNeverAskAgain | | 注解在当用户选中了授权窗口中的不再询问复选框后并拒绝了权限请求时需要调用的方法,一般可以向用户解释为何申请此权限,并根据实际需求决定是否再次弹出权限请求对话框 |
3.2、两个interface
库中定义了两个接口,一个是PermissionRequest接口
/**
* Interface used by {@link OnShowRationale} methods to allow for continuation
* or cancellation of a permission request.
*/
public interface PermissionRequest {
void proceed();
void cancel();
}
GrantableRequest接口用处不详,作者可能以后要用吧。
public interface GrantableRequest extends PermissionRequest {
void grant();
}
3.3、一个PermissionUtils
permissions.dispatcher包中唯一的java代码PermissionUtils类,这里面封装了权限判断、申请权限的各种方法。在注解自动生成的代理类中调用该工具类中的方法。
4、用法
粗略看了下权限申请库的组成要素。它们怎么用呢?很简单,通过注解将原来的代码简化。
4.1、@RuntimePermissions
在Activity或Fragment上用@RuntimePermissions注解是必须的,标注在要申请权限的Activity或者Fragment上
@RuntimePermissions
public class PermissionActivity extends AppCompatActivity {
...
}
后面所有相关的方法都要在同一个Activity或者Fragment中。
4.2、@NeedsPermission
当某个方法需要特定的权限时(动态申请的权限),在该方法上用注解@NeedsPermission,带上需要的权限。系统中的权限都在Manifest中有预设值。
@NeedsPermission({Manifest.permission.CALL_PHONE})
public void makeCall() {
//
}
4.3、@OnPermissionDenied
当申请的权限被拒绝时,希望调用该方法执行相应的操作或者弹框、Toast提醒用户,用@OnPermissionDenied注解需要调用的方法。
@OnPermissionDenied({Manifest.permission.CALL_PHONE})
public void onCallPermissionDenied() {
//
}
4.4、@OnShowRationale
申请权限被拒绝,第二次再次申请权限时希望弹出说明:告诉用户应用为什么需要申请这个权限,使用。注意,多次被拒绝后,系统可能不会再让应用重复申请,类似never ask again。
4.5、@OnNeverAskAgain
标注如果权限请求失败,而且用户勾选不再询问的时候执行的方法。
@OnNeverAskAgain({Manifest.permission.CALL_PHONE, Manifest.permission.SEND_SMS})
public void onNeverAskAgain() {
//
}
4.6、辅助类 ***PermissionsDispatcher
用注解标注好我们的方法后,重新rebuild一下项目,会在build/generated/ap_generated_sources/debug/out/包名目录下生成辅助类***PermissionsDispatcher。名称就是@RuntimePermissions注解的Activity或者Fragment类名加上PermissionsDispatcher后缀。
生成的辅助类代码其实也不多,替开发者完成很多工作:权限的检测、申请以及回调(这个需要开发者在原有的回调方法中手动替换)。
final class PermissionActivityPermissionsDispatcher {
private static final int REQUEST_MAKECALL = 0;
private static final String[] PERMISSION_MAKECALL = new String[] {"android.permission.CALL_PHONE"};
private PermissionActivityPermissionsDispatcher() {
}
static void makeCallWithPermissionCheck(@NonNull PermissionActivity target) {
if (PermissionUtils.hasSelfPermissions(target, PERMISSION_MAKECALL)) {
target.makeCall();
} else {
ActivityCompat.requestPermissions(target, PERMISSION_MAKECALL, REQUEST_MAKECALL);
}
}
static void onRequestPermissionsResult(@NonNull PermissionActivity target, int requestCode,
int[] grantResults) {
switch (requestCode) {
case REQUEST_MAKECALL:
if (PermissionUtils.verifyPermissions(grantResults)) {
target.makeCall();
} else {
target.onCallPermissionDenied();
}
break;
default:
break;
}
}
}
4.7、最后一步 替换
这个权限申请库也不是完全的零入侵,需要开发者修改部分项目代码才行:
①在调用原本需要权限的方法,把调用@NeedsPermission注解的方法替换成辅助类中的***WithPermissionCheck()方法
PermissionActivityPermissionsDispatcher.makeCallWithPermissionCheck(this);
②重写onRequestPermissionsResult()方法,用辅助类接管
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
}
5、对比
实际看看用权限动态申请框架和没用两者代码的差别,不仅在代码量上,而且在可读性上。
5.1、使用前
让我们用传统的权限动态检测、申请方式实现一遍。代码较多,而且较乱。
public class PermissionActivity extends AppCompatActivity {
private static final int CALL_PHONE_PERMISSION_REQUEST_CODE = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_permission);
if (PermissionChecker.checkSelfPermission(this, Manifest.permission.CALL_PHONE)
== PermissionChecker.PERMISSION_GRANTED) {
makeCall();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE},
CALL_PHONE_PERMISSION_REQUEST_CODE);
}
}
public void makeCall() {
//
}
public void onCallPermissionDenied() {
//
}
public void onNeverAskAgain() {
//
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == CALL_PHONE_PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
makeCall();
}
} else {
onCallPermissionDenied();
}
}
}
5.2、使用后
需要编写的代码缩减很多,而且更整洁,一目了然。
@RuntimePermissions
public class PermissionActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_permission);
PermissionActivityPermissionsDispatcher.makeCallWithPermissionCheck(this);
}
@NeedsPermission({Manifest.permission.CALL_PHONE})
public void makeCall() {
//
}
@OnPermissionDenied({Manifest.permission.CALL_PHONE})
public void onCallPermissionDenied() {
//
}
@OnNeverAskAgain({Manifest.permission.CALL_PHONE, Manifest.permission.SEND_SMS})
public void onNeverAskAgain() {
//
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
}
}
相关资料:
github/permissions-dispatcher/PermissionsDispatcher
PermissionsDispatcher使用详解
初探权限库PermissionsDispatcher
PermissionsDispatcher动态权限申请