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 条评论