网上有很多关于动态权限检查的开源库,使用方式呢都大同小异。之前也做过一个动态权限检查的功能,通过注解的形式来进行检查,现在把思路做个分享。
1.使用到的开源库
1. AOP思想的AspectJ (面向切面思想的解决方案)
2. EasyPermissions 开源库 (动态权限检查库)
AOP是Aspect Oriented Programming的缩写,即『面向切面编程』。它和我们平时接触到的OOP都是编程的不同思想,OOP,即『面向对象编程』,它提倡的是将功能模块化,对象化,而AOP的思想,则不太一样,它提倡的是针对同一类问题的统一处理。AspectJ实际上是对AOP编程思想的一个实践。我们可以利用AspectJ,把用来修饰方法的注解作为一个切点,在执行该方法时,触发该切点所对应的逻辑。
AspectJ的使用:https://blog.csdn.net/eclipsexys/article/details/54425414 (大家可以通过此链接了解一下aspectj的使用)
EasyPermissions呢,时一个动态权限检查的开源库,大大简化了Android端对动态权限问题的代码编写。其实其他的开源库也一样可以实现我的思路,在此用EasyPermissions作为一个例子。
EasyPermissions github地址:https://github.com/jiezongnewstar/easypermissions
2.直接上代码吧
首先,创建注解PermissionCheck,Target为修饰方法,Retention为运行时,其中设置一个成员变量,以用来传入需要检查的权限信息。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionCheck {
public String[] checkString();
}
另外我们需要创建一个被@Aspect修饰的类来对PermissionCheck注解做切点,以及切入后的逻辑编写。类中比如@Pointcut
@Around等注解的用法等,可以参考上面的链接,对AspectJ的使用熟悉熟悉。@Pointcut,就是设置一个切入点。类中的意思就是被PermissionCheck的注解所修饰的方法作为一个切点。@Around,就是在执行被PermissionCheck注解修饰的方法时,进入切点,并执行check方法。
@Aspect
public class PermissionCheckAspectJ {
private static final String TAG = "PermissionCheck";
@Pointcut("execution(@com.sound.base.aspectj.PermissionCheck * *(..)) && @annotation(ann)")
public void checkPermission(PermissionCheck ann) {
}
@Around("checkPermission(permissioncheck)")
public Object check(ProceedingJoinPoint joinPoint, PermissionCheck permissioncheck) throws Throwable {
return joinPoint.proceed();
}
}
好,接下来我们就需要编写check方法,来对注解所传过来的权限进行检查。
首先就是对参数做个判空,以保证代码完整性与安全性,try catch也是必要的。由于EasyPermissions的api的使用需要上下文参数,所以必须先通过joinPoint.getThis()来获取到当前方法所在的类实例中。由于切点既有可能在Activity中,也有可能在Fragment中,所以我们要对joinPoint.getThis()做个区分,通过不同方式获取到context。
@Around("checkPermission(permissioncheck)")
public Object check(ProceedingJoinPoint joinPoint, PermissionCheck permissioncheck) throws Throwable {
if (null != permissioncheck) {
Context context = null;
try {
if (joinPoint.getThis() instanceof android.support.v4.app.Fragment) {
System.out.println("Fragment");
android.support.v4.app.Fragment fragment = (android.support.v4.app.Fragment) joinPoint.getThis();
context = fragment.getActivity();
} else {
context = (Context) joinPoint.getThis();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return joinPoint.proceed();
}
接下来就需要调用EasyPermissions的api来进行权限的检查。看代码即可。
@Around("checkPermission(permissioncheck)")
public Object check(ProceedingJoinPoint joinPoint, PermissionCheck permissioncheck) throws Throwable {
if (null != permissioncheck) {
Context context = null;
try {
if (joinPoint.getThis() instanceof android.support.v4.app.Fragment) {
android.support.v4.app.Fragment fragment = (android.support.v4.app.Fragment) joinPoint.getThis();
context = fragment.getActivity();
} else {
context = (Context) joinPoint.getThis();
}
} catch (Exception e) {
e.printStackTrace();
}
if (null != context) {
String[] checkStrings = permissioncheck.checkString();
int code = PermissionCode.getCode(checkStrings);
String string = PermissionCode.getInfo(checkStrings);
if (EasyPermissions.hasPermissions(context, checkStrings)) {
return joinPoint.proceed();
} else {
if (joinPoint.getThis() instanceof Activity) {
Activity activity = (Activity) joinPoint.getThis();
EasyPermissions.requestPermissions(activity, string, code, checkStrings);
} else if (joinPoint.getThis() instanceof android.support.v4.app.Fragment) {
Fragment fragment = (android.support.v4.app.Fragment) joinPoint.getThis();
EasyPermissions.requestPermissions(fragment, string, code, checkStrings);
}
return null;
}
}
}
return joinPoint.proceed();
}
代码的意思就是,如果检查当前是拥有对应权限的,则执行joinPoint.proceed()方法来让注解修饰了的方法继续执行下去。如果说并没有对应权限,则会打开权限请求的弹框,通知用户去分配权限,return null 让方法不继续执行下去。
这两个编写好后,最重要的代码基本就编写完成了,为了更加方便使用,我们可以通过编写BaseActivity和BaseFragment来更加简化我们的使用。
base代码中的onRequestPermissionResult中调用EasyPermissions的api,并实现EasyPermissions的两个方法。
@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);
}
base页面实现PermissionCallbacks,并实现对应的两个方法,在方法中调用我们自己编写的抽象方法,暴露给子类页面去使用。
public abstract class BaseActivity extends NetObserverAppCompatActivity implements EasyPermissions.PermissionCallbacks {
public abstract void permissionSuccess(int requestCode, List<String> perms);
public abstract void permissionFaild(int requestCode, List<String> perms);
@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
permissionSuccess(requestCode, perms);
}
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
permissionFaild(requestCode, perms);
}
那么,代码就编写完成了,我们可以看下如何使用了。
比如我们要打开相机拍照并获取照片,则需要相机权限,存储的读写权限。
首先,可以创建一个cameraPermission的方法,用来调用打开相机。然后用PermissionCheck注解设置我们所需要检查的权限。
@PermissionCheck(checkString = {PermissionCode.CAMERA_PERMISSION, PermissionCode.WRITE_EXTERNAL_STORAGE_PERMISSION, PermissionCode.READ_EXTERNAL_STORAGE_PERMISSION})
public void cameraPermission() {
CameraUtils.getInstance().openAlbum(this);
}
然后去实现base中的两个抽象方法
@Override
public void permissionSuccess(int requestCode, List<String> perms) {
cameraPermission();
}
@Override
public void permissionFaild(int requestCode, List<String> perms) {
Toast.makeText(this, "用户已拒绝相机权限", Toast.LENGTH_SHORT).show();
}
当用户给与了权限之后,就会调用到permissionSuccess方法,从而打开了相机。当用户拒绝了权限请求,就会弹出对应吐司来提示用户。
这些都编写完成之后,那么就可以调用方法了。
} else if (i == R.id.check_camera_permission_button) {
cameraPermission();
} else if (i == R.id.arouter_jump_activity) {
我们只需要调用cameraPermission()就ok了,所有的权限检查等等就在上面的编写过程中一一处理了,还是比较方便的。这样代码也相对比较整洁。
希望能给大家带来一些帮助,谢谢。