AspectJ in Android 使用初探 二

在 AspectJ in Android 使用初探 一 中介绍了基本概念以及如何集成到 Android 项目中,在这里具体化 AspectJ 的具体使用。

在介绍具体的使用方法前,明确几个基本的概念帮助理解

一、AspectJ 语法

1.1 Join Points (连接点)

Join Point 就是程序中可能作为代码注入目标的特定的点,例如一个方法调用或者方法入口。被 Join Point 标记的代码位置就是你想把新的代码插在程序的那一个地方,理论上一个程序中很多地方都可以被看做是 Join Point,但是 AspectJ 中,只有下面几种几种方法可以被标记为 Join Point

Join Point 说明 实例
method call 函数调用 比如调用Log.e(),这是一处JPoint
method execution 函数执行 比如Log.e()的执行内部,是一处JPoint。注意它和method call的区别。method call是调用某个函数的地方。而execution是某个函数执行的内部。
constructor call 构造函数调用 和method call类似
constructor execution 构造函数执行 和method execution类似
field get 获取某个变量 比如读取DemoActivity.debug成员
field set 设置某个变量 比如设置DemoActivity.debug成员
pre-initialization Object在构造函数中做得一些工作。 很少使用,详情见下面的例子
initialization Object在构造函数中做得工作 详情见下面的例子
static initialization 类初始化 比如类的static{}
handler 异常处理 比如try catch(xxx)中,对应catch内的执行
advice execution 这个是AspectJ的内容,稍后再说

1.2 Point Cuts (切入点)

上面介绍了 Join Points ,但是在项目中并不是要拿所有的 Joint Points 作切面,关于如何选择 Join Points 作为我们切入点就是现在要谈到的 Point Cuts 概念。

Point Cuts 的基本语法如下:

@注解 访问权限 返回值类型 类名 函数名 参数

具体结合 Join Points 使用方法如下

Joint Point 说明 Pointcut语法说明
method execution 一般指定方法的执行 execution(MethodSignnature)
method call 函数被调用 call(MethodSignnature)
constructor call 构造函数被调用 call(ConstructorSignature)
constructor execution 构造函数执行内部 execution(ConstructorSignature)
field get 读变量 get(FieldSIgnature)
field set 写变量 set(FieldSIgnature)
handler 异常处理 handler(TypeSignature) 注意:只能和@Before()配合使用,不支持@After、@Around等
advice execution advice执行 adciceexectuin()

具体 Signature 参考

Sigbature 语法(间隔一个空格)
MethodSignature @注解 访问权限 返回值类型 类名.函数名(参数)
ConstructorSignature @注解 访问权限 类名.new(参数)
FieldSignature @注解 访问权限 变量类型 类名.类成员变量名

相关通配符

通配符 意义 举个栗子
* 用于匹配除.号之外的任意字符 *Model:以Model结尾的子类,比如TabelModel,TreeModel
.. 表示任意子package java..* :表示java任意子类
+ 表示子类 java..*Model+:表示Java任意package中名字以Model结尾的子类,比如TabelModel,TreeModel
(..) 代表参数个数和类型都是任意

上面是几种 Point Cuts 的语法,但是就如 Java 语言中的 “&&”、“||”、“!” 一样,各个 Point Cuts 之间可以有以上操作,并且也有一些其他的具体语法进行 条件过滤, 具体有以下几个操作符可供选择:

关键词 说明 示例
within(TypePattern) TypePattern标示package或者类。TypePatter可以使用通配符 表示某个Package或者类中的所有JPoint。比如within(Test):Test类中(包括内部类)所有JPoint。图2所示的例子就是用这个方法。
withincode(Constructor Signature、Method Signature) 表示某个构造函数或其他函数执行过程中涉及到的JPoint withinCode(* TestDerived.testMethod(..)) 表示testMethod涉及的JPoint、withinCode( *.Test.new(..))表示Test构造函数涉及的JPoint
cflow(pointcuts) cflow是call flow的意思 cflow的条件是一个pointcut cflow(call TestDerived.testMethod):表示调用TestDerived.testMethod函数时所包含的JPoint,包括testMethod的call这个JPoint本身
cflowbelow(pointcuts) cflow是call flow的意思。 cflowblow(call TestDerived.testMethod):表示调用TestDerived.testMethod函数时所包含的JPoint,不包括testMethod的call这个JPoint本身
this(Type) JPoint的this对象是Type类型。(其实就是判断Type是不是某种类型,即是否满足instanceof Type的条件) JPoint是代码段(不论是函数,异常处理,static block),从语法上说,它都属于一个类。如果这个类的类型是Type标示的类型,则和它相关的JPoint将全部被选中。this(TestDerived)将会选中这个testMethod JPoint
target(Type) JPoint的target对象是Type类型 和this相对的是target。不过target一般用在call的情况。call一个函数,这个函数可能定义在其他类。比如testMethod是TestDerived类定义的。那么target(TestDerived)就会搜索到调用testMethod的地方。但是不包括testMethod的execution JPoint
args(TypeSignature) 用来对JPoint的参数进行条件搜索的 比如args(int,..),表示第一个参数是int,后面参数个数和类型不限的JPoint。

### 1.3 Advice
具体可以理解为定义操作行为运行在切面的具体位置(前、后、包裹),具体操作符如下:

关键词 说明 示例
before() before advice 表示在JPoint执行之前,需要干的事情
after() after advice 表示JPoint自己执行完了后,需要干的事情。
返回值类型 around() before和around是指JPoint执行前或执行后备触发,而around就替代了原JPoint around是替代了原JPoint,如果要执行原JPoint的话,需要调用proceed
after():returning(返回值类型)、after():throwing(异常类型) returning和throwing后面都可以指定具体的类型,如果不指定的话则匹配的时候不限定类型 假设JPoint是一个函数调用的话,那么函数调用执行完有两种方式退出,一个是正常的return,另外一个是抛异常。注意,after()默认包括returning和throwing两种情况

AspectJ 使用

上面的语法操作符看起来比较抽象,看起来也比较无聊。在 Coding World 中还是需要具体的 Code 来直观的说明一切。具体的代码可以参看 AspectJDemo

场景一

针对 Activity 的相关生命周期做切面处理

    @PonitCut("execution(* android.app.Activity.onC*(..))")
    public void setCut(){}

   @Before("setCut()")
    public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
        String key = joinPoint.getSignature().toString();
        Log.e(TAG, "onActivityMethodBefore: 切面的点执行了!" + key);
    }

当然你也可以这样写:

@Before("execution(* android.app.Activity.onC*(..))")
    public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
        String key = joinPoint.getSignature().toString();
        Log.e(TAG, "onActivityMethodBefore: 切面的点执行了!" + key);
    }

运行程序我们可以看到如下 Log 日志;

这里写图片描述

当然针对于 Before 、After、Around 、excution 、call 等具体区别此处就不展开写了,毕竟网上的博客写的都差不多,复习此知识点时可浏览参考博客链接。

场景二: 注解的使用

对申请手机权限的切面做处理,具体代码如下:
注解类

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityCheckAnnotation {
    public String declaredPermission();  
}

切面处理下关代码:

    /**
     * 注意: 这里有一个需要注意的点:
     *   SecurityCheckAnnotation 相应的注解类如果和该类在同包,则可以直接写SecurityCheckAnnotation,
     *   如果不在则写SecurityCheckAnnotation全路径
     */
    @Pointcut("execution(@com.mk.aline.aspectjdemo.SecurityCheckAnnotation public * *..*.*(..)) && @annotation(ann)")
    public void checkPermssion(SecurityCheckAnnotation ann) {
    }


    /**
     * 接下来是advice,advice的真正功能由check函数来实现,这个check函数第二个参数就是我们想要
     * 的注解。在实际运行过程中,AspectJ会把这个信息从JPoint中提出出来并传递给check函数。
     */
    @After("checkPermssion(securityCheckAnnotation)")
    public void check(JoinPoint joinPoint, SecurityCheckAnnotation securityCheckAnnotation) {
        //从注解信息中获取声明的权限。
        String neededPermission = securityCheckAnnotation.declaredPermission();
        Log.e(TAG, joinPoint.toShortString());
        Log.e(TAG, "\tneeded permission is " + neededPermission);
        return;
    }

业务类(进行权限申请)

@SecurityCheckAnnotation(declaredPermission = "android.permission.READ_PHONE_STATE")
    public void checkPhoneState() {
        //如果不使用AOP,就得自己来检查权限
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.READ_PHONE_STATE)
                != PackageManager.PERMISSION_GRANTED) {
            Log.e(TAG, "have no permission to read phone state");
            return;
        }
        Log.e(TAG, "Read Phone State succeed");
        return;
    }

具体打印日志:

这里写图片描述

注意: 这里特别需要的一个注意点见上面的注释信息

业务使用场景

熟悉了 AspectJAndroid 中基本使用,那么具体贴近业务怎样巧妙的使用 AspectJ 才能够达到事半功倍的效果,由于我司的项目中并没有使用到该项技术,下面我贴几个在网上可以看到了实际运用的的帖子,以供参考:
Android中的AOP编程之AspectJ实战实现数据埋点
归纳AOP在Android开发中的几种常见用法
Android基于AOP的非侵入式监控之——AspectJ实战
AOP实现Android集中式登录架构

以上内容为自己的学习笔记,自己会进一步研究 AspectJ 在 Android 项目中的使–用,笔记也会继续更新。


参考资料

深入理解Android之AOP
看AspectJ在Android中的强势插入
Aspect Oriented Programming in Android
Android中的AOP编程之AspectJ实战实现数据埋点

扩展阅读

Android AOP编程的四种策略探讨:Aspectj,cglib+dexmaker,Javassist,epic+dexposed

你可能感兴趣的:(Android,开发)