Aspectj与AndroidM Permission结合

861 阅读3分钟

前言
这里我们开始使用AspectJ来实现权限的申请和相关操作

上几篇文章已经讲过了 AspectJ的语法和配置和Android M 中的权限的申请

大致思路:1.找到权限申请的切点 >>> 2.插入权限申请的判断

1.切点

一般来说我们申请权限写在一个方法内方便我们Hook,我们使用注解(Annotation)方式来实现,如果我们把一个具体的方法当着切点扩展性差而且使用也不方便。

我们定义一个Annotation来当着申请权限的切入点

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckPermission {
    String[] value();  //权限申请
    int requestcode() default 10000; //后面讲
}

2.开始切入

如果你有AspectJ切入的经验或者看过 Annotation的切入 那看客可以继续往下看了

看客们能到这一步,大概应该都知道我们需要Hook到一个方法的是否可以让它执行到,我们应该用Around环绕模式来PointCut这个点

@Pointcut("execution(@com.zdg.aspectj.checkpermission.annotition.CheckPermission !synthetic * *(..))")
public void findCheckPermission() {}
@Around("findCheckPermission()")
public  void checkPermission(ProceedingJoinPoint proceedingJoinPoint) {}

这样我们大概也就切入了需要申请权限这个点了,接下来我们就来处理 申请权限的具体操作

Android M的Permission我们还需要了解一些基本知识Android 6.0以上的Permission

接下来我们具体实现 checkPermission(ProceedingJoinPoint proceedingJoinPoint),话不多说,就是干。

@Around("findCheckPermission()")
public  void checkPermission(ProceedingJoinPoint proceedingJoinPoint) {
    Object target = proceedingJoinPoint.getTarget();//调用使用CheckPermission注解的类对象
    MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
    CheckPermission annotation = signature.getMethod().getAnnotation(CheckPermission.class);//获取该方法注解中的需要申请的权限
    if(PermissionChecker.checkSelfPermission((Context)target, Arrays.asList(annotation.vaule()))==PermissionChecker.PERMISSION_GRANTED/*判断权限是否同意了*/){
        try {
            proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
        } catch (Throwable throwable) {
                throwable.printStackTrace();
        }
    }
}

以上只是实现了一个最简单的判断逻辑。 然而权限的申请往往不是这样简单?????😎😎😎😎😎😎,我们知道申请权限的过程是一个异步的,申请的过程中会弹出系统对话框,用户选择操作后会回调方法onRequestPermissionsResult,第一次申请是异步的回调onRequestPermissionsResult后判断同意继续执行同意后的操作。

问题就来了, 一个异步的过程我们怎么切入到一个方法中的????想出了几个方案。

问题总结:

问题一:权限申请是异步操作,@CheckPermission 回调onRequestPermissionsResult后怎么样回到设有@CheckPermission执行
问题二:开发者权限申请过程中为了方便肯定不愿意自己实现onRequestPermissionsResult,自己实现了,代码量和自己写权限申请的代码量差不多了, 为嘛还是用你这个SDK????

方案一:继续切入onRequestPermissionsResult

  • 放弃理由:1.如果切入的当前类没有复写改方法,AspectJ无法切入改方法,2.怎么样让两个切入点,执行同一套方法(一个是同步的, 一个是异步的)3.接入代码量和使用原始方法申请没有多大的区别

方案二:在方案一的基础上改变套路,申请权限启动内部的一个activity 在这个activity中实现onRequestPermissionsResult,并且切入 onRequestPermissionsResult ,这样解决了减少代码量。

以上两个方案解决了《问题二》的问题。

至于《问题一》暂时想到的方案是在Aspect类保存请求权限时(通过过注解切入权限的时)保存JoinPoint这个对象,当onRequestPermissionsResult切入点被执行时 权限通过时 获取保存的JoinPoint来继续执行 该(@CheckPermission)切入点的方法。

大概就是这个思路:

@Aspect
public class CheckPermissionRuntime {
     ProceedingJoinPoint mPermissionJoinPoint; 
    @Around("findCheckPermission()")
    public  void checkPermission(ProceedingJoinPoint proceedingJoinPoint) {
        this.mPermissionJoinPoint = proceedingJoinPoint;  //解决《问题一》
        if(/*需要申请权限*/){
            startActivity()
        }else{
            try {
                 proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
            
    }
    @Before("onRequestPermissionsResult()")
    public void onRequestPermissionsResult(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        int requestcode = (int) args[0];
        String[] permissions= (String[]) args[1];
        int[] grantResults= (int[]) args[2];
        if(/*判断所有权限已经都同意了*/){
            try {
                 mPermissionJoinPoint.proceed(mPermissionJoinPoint.getArgs());
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
    }
}

以上就是大致的实现方式。

还有一些细节:

1.因为处理 ` CheckPermissionRuntime `是一个单例 而proceedingJoinPoint会持有activity的对象,所以用不到的时候最好置空,
2.activity与fragment申请权限的不同方式。
-------------本文结束感谢您的阅读-------------