0
点赞
收藏
分享

微信扫一扫

从Android优雅权限框架理解AOP思想(2) 原理篇

前言

正文大纲


正文

承接上文 从Android优雅权限框架理解AOP思想(1) 表层篇

AOP思想回顾


AOP 实现方式一览

三个AOP切入时机


AspectJ原理详解

现象

Demo地址:https://github.com/18598925736/GracefulPermissionFramework/commits/dev_aspectJ

public class LocationUtil {

    @PermissionNeed(
            permissions = {Manifest.permission.BODY_SENSORS},
            requestCode = 0)
    public void getLocation() {
        Log.d("LocationUtilTag","普通Java类:权限已获得");
        ToastUtil.showToast("普通Java类:权限已获得");
    }

    /**
     * 这里写的要特别注意,denied方法,必须是带有一个int参数的方法,下面的也一样
     *
     * @param requestCode
     */
    @PermissionDenied
    private void denied(int requestCode) {
        Log.d("LocationUtilTag","普通Java类:权限被拒绝");
        ToastUtil.showToast("普通Java类:权限被拒绝");
    }

    @PermissionDeniedForever
    private void deniedForever(int requestCode) {
        Log.d("LocationUtilTag","普通Java类:权限被永久拒绝");
        ToastUtil.showToast("普通Java类:权限被永久拒绝");
    }
}
  1. Apk反编译,看一眼 apk dex里面的LocationUtil这个类变成了什么样儿。
    (推荐一个apk反编译工具 jadx-0.6.1,可以直接看到apk的dex源代码,但,如果混淆了,你看到的将会是混淆之后的源码.)
public class LocationUtil {
    private static /* synthetic */ Annotation ajc$anno$0;
    private static final /* synthetic */ StaticPart ajc$tjp_0 = null;

    public class AjcClosure1 extends AroundClosure {
        public AjcClosure1(Object[] objArr) {
            super(objArr);
        }

        public Object run(Object[] objArr) {
            objArr = this.state;
            LocationUtil.getLocation_aroundBody0((LocationUtil) objArr[0], (JoinPoint) objArr[1]);
            return null;
        }
    }

    static {
        ajc$preClinit();
    }

    private static /* synthetic */ void ajc$preClinit() {
        Factory factory = new Factory("LocationUtil.java", LocationUtil.class);
        ajc$tjp_0 = factory.makeSJP(JoinPoint.METHOD_EXECUTION, factory.makeMethodSig("1", "getLocation", "com.zhou.graceful.LocationUtil", "", "", "", "void"), 20);
    }

    static final /* synthetic */ void getLocation_aroundBody0(LocationUtil ajc$this, JoinPoint joinPoint) {
        joinPoint = "普通Java类:权限已获得";
        Log.d("LocationUtilTag", joinPoint);
        ToastUtil.showToast(joinPoint);
    }

    @PermissionNeed(permissions = {"android.permission.BODY_SENSORS"}, requestCode = 0)
    public void getLocation() {
        JoinPoint makeJP = Factory.makeJP(ajc$tjp_0, this, this);
        PermissionAspect aspectOf = PermissionAspect.aspectOf();
        ProceedingJoinPoint linkClosureAndJoinPoint = new AjcClosure1(new Object[]{this, makeJP}).linkClosureAndJoinPoint(69648);
        Annotation annotation = ajc$anno$0;
        if (annotation == null) {
            annotation = LocationUtil.class.getDeclaredMethod("getLocation", new Class[0]).getAnnotation(PermissionNeed.class);
            ajc$anno$0 = annotation;
        }
        aspectOf.doPermission(linkClosureAndJoinPoint, (PermissionNeed) annotation);
    }

    @PermissionDenied
    private void denied(int requestCode) {
        String str = "普通Java类:权限被拒绝";
        Log.d("LocationUtilTag", str);
        ToastUtil.showToast(str);
    }

    @PermissionDeniedForever
    private void deniedForever(int requestCode) {
        String str = "普通Java类:权限被永久拒绝";
        Log.d("LocationUtilTag", str);
        ToastUtil.showToast(str);
    }
}

经过两方对比,我们发现AspectJ给我们原本的LocationUtil.java源码中做的改动为:

从结论上来看,AspectJ进行AOP的手段为: 在我们编写好源码之后,改动 class字节码,让程序执行的逻辑发生了变化.

  1. 还是jadx,现在看看整个工程的源码目录结构。

小结

那么随之而来,就有下一个疑问,AspectJ是按照什么思路重构我们原有的代码逻辑的
但是在此之前,现象背后有几个概念先声明:

现象背后

在此感谢博主:https://blog.csdn.net/woshimalingyi/article/details/73252013 ,网上翻了很多博客,就这个写的比较完善。

概念

  • Aspect切面
@Aspect // 把一个类定义为切面类
public class PermissionAspect {//  权限切面类,集中处理所有权限问题
  //...
}
  • PointCut 切入点
  • JoinPoint 连接点
  • Advise 切入策略
    我们有了切入点和连接点,现在需要确定 我们要插入的逻辑以什么样的方式和原代码逻辑合并

现在来看看AspectJ对我们的LocationUtil类做了什么

按照这个类被加载时的执行顺序来看:
首先是 静态代码块:

    static {
        ajc$preClinit();
    }

    private static /* synthetic */ void ajc$preClinit() {
        Factory factory = new Factory("LocationUtil.java", LocationUtil.class);
        ajc$tjp_0 = factory.makeSJP(JoinPoint.METHOD_EXECUTION, 
            factory.makeMethodSig("1", "getLocation", "com.zhou.graceful.LocationUtil", "", "", "", "void"), 20);
    }

在JVM开始创建这个类的class对象的时候,ajc$preClinit()方法便开始执行。
创建了一个Factory,然后用Factory.makeSJP()方法创建了StaticPart.
读一遍代码,可以发现:

factory.makeMethodSig(...) 返回的是MethodSignature,也就是用于标记Method的签名。

可以作结论了,第一步,AspectJ框架,先通过注入代码的方式,确定了LocationUtil类的PointCut切入点,切入点就是getLocation方法,并且最终创建了一个静态的StaticPart ajc$tjp_0对象,保存了起来(其实它就是一个中间产物"静态部分信息",后续将会用它来创建JointPoint对象).
我们继续。
发现一个奇怪的东西: 闭包

   public class AjcClosure1 extends AroundClosure {
        public AjcClosure1(Object[] objArr) {
            super(objArr);
        }

        public Object run(Object[] objArr) {
            objArr = this.state;
            LocationUtil.getLocation_aroundBody0((LocationUtil) objArr[0], (JoinPoint) objArr[1]);
            return null;
        }
    }
    static final /* synthetic */ void getLocation_aroundBody0(LocationUtil ajc$this, JoinPoint joinPoint) {
        joinPoint = "普通Java类:权限已获得";
        Log.d("LocationUtilTag", joinPoint);
        ToastUtil.showToast(joinPoint);
    }

这个闭包把我们原先的getLocation()方法的方法体,重新封装起来了。既然是封装起来,那么 肯定是要使用的。

继续往下走,进入改过之后的getLocation()

    @PermissionNeed(permissions = {"android.permission.BODY_SENSORS"}, requestCode = 0)
    public void getLocation() {
        JoinPoint makeJP = Factory.makeJP(ajc$tjp_0, this, this); 
        PermissionAspect aspectOf = PermissionAspect.aspectOf();
        ProceedingJoinPoint linkClosureAndJoinPoint = new AjcClosure1(new Object[]{this, makeJP}).linkClosureAndJoinPoint(69648);
        Annotation annotation = ajc$anno$0;
        if (annotation == null) {
            annotation = LocationUtil.class.getDeclaredMethod("getLocation", new Class[0]).getAnnotation(PermissionNeed.class);
            ajc$anno$0 = annotation;
        }
        aspectOf.doPermission(linkClosureAndJoinPoint, (PermissionNeed) annotation);
    }

做个结论

特别注意

而 参数args写法 则是 如下:

预计接下来研究方向


结语

举报

相关推荐

0 条评论