AspectJ 在 Android 中的使用

本文介绍了AspectJ在Android中的使用,包括AOP编程思想、AspectJ语法、集成到Android Studio的方法。讲解了JoinPoint、Pointcut、Advice的概念,并通过Library库依赖和Plugin插件两种方式展示了AspectJ在Android项目中的实际应用。示例代码演示了如何监听成员变量赋值变化,帮助开发者理解AspectJ在日志系统和权限管理等方面的应用。
摘要由CSDN通过智能技术生成

AspectJ 在 Android 中的使用

在介绍 AspectJ 之前,我们先看看常见的几种编程架构思想。

  • 面向对象编程 Object Oriented Programming
  • 面向过程编程 Procedure Oriented Programming
  • 面向切面编程 Aspect Oriented Programming

面向对象、面向过程、面向切面, 这三种是我们常见的三种编程架构思想,在日常的编程中, OOP 是 Android 开发中最常见的,其他的两种比较少见。

原创AspectJ 在 Android 中的使用

一、AOP

AOP 是面向切面编程,它在我们的日志系统、权限管理方面有着比较好的应用。
在项目中,我们的很多功能都是分散到各个模块,例如日志打印,AOP 的目标就是要把这些功能集中起来,放到一个统一的地方来控制和管理。

二、AspectJ

AOP 是一种编程的思想,在具体的编程中需要实际的工具去实现这套思想。 AspectJ 就是这样的一个工具。使用 AspectJ 有两种方式

    1. 完全使用 AspectJ 的语言开发;
    1. 使用 AspectJ 注解,完全的使用纯 Java 开发

我们后续讲的,基本上都是以 AspectJ 注解的方法,同时在最后也会附上 AspectJ 和 AspectJ 注解的等价。

1.AspectJ 语法

这里只是介绍简单的一些概念,如果想要去了解深入的用法,可参考文后的链接,去官网查看。

1. JoinPoint

JoinPoint: A particular point in a program that might be the target of code injection.
JoinPoint 简单一点说就是程序运行时要执行一些动作的点。

AspectJ 中可以选择的 JoinPoint

JoinPoint 说明 示例
method call 函数调用 例如调用 Log.e( )
method execution 函数执行 例如 Log.e( ) 的执行内部。
method call 是调用某个函数的地方
execution 是某个函数执行的内部
constructor call 构造函数调用 和 method call 类似
constructor execution 构造函数执行 和 method execution 类似
field get 获取某个变量 例如读取 MainActivity.mTest 成员
field set 设置某个变量 例如设置 MainActivity.mTest 成员
pre-initialization Object 在构造函数中做的一些工作
initialization Object 在构造函数中做的工作
static initialization 类初始化 例如类的 static{}
handler 异常处理 例如 try catch(xxx) 中,对应 catch 内的执行
advice execution AspectJ 的内容

JoinPoint 的选择要结合下面的 Pointcuts 表达式来看

2. Pointcut

Pointcut: An expression which tell a code injection tool where to inject a particular piece of code
Pointcut 简单的说就是从一堆的 JoinPoint 中挑选感兴趣的 JoinPoint 的表达式。

例如

pointcut anyCall(): call(* *.println(…)) && !within(TestAspect);

在 AspectJ 的语言中定义一个 Pointcout 需要用关键词 pointcut .
上面的这里是

  • pointcut: 是定一个 Pointcut 的关键词
  • anyCall(): 是 Pointcut 的名称
  • call : 表示 JoinPoint 的类型为 call
  • 第一个 ‘*’ 号是返回值, ‘*’ 代表是任意返回值; 第二个 ‘*’ 号代表是包名,‘*’ 代表是任意包名,这边表明我们是选择任意包名下的 println 函数;在 (…) 中指定参数类型,‘…’ 通配符表示任意类型;
  • &&! 表示组合条件,有 &&, || 以及 !
  • within(TestAspect): within 是 JoinPoint 间接选择过滤的一个方法,后面会讲到。 !within(TestAspect) 表示调用者的类型不是 TestAspect.
3. JointPoint 的选择

JointPoint 的选择有分成直接选择和间接选择两种方式

  • JointPoint 的直接选择就是通过和 Pointcut 的语法一一对应关系中选择;
  • JointPoint 的间接选择就是通过一些通配符进行筛选过滤的选择,上面例子中的 within 就是间接选择的一种。
1.JointPoint 直接选择

JoinPoint 的选择策略和 Pointcut 的语法对应关系

JoinPoint Category Pointcut Syntax
Method execution execution(MethodSignature)
Method call call(MethodSignature)
Constructor execution execution(ConstructorSignature)
Constructor call call(ConstructorSignature)
Class initialization staticinitialization(TypeSignature)
Field read access get(FieldSignature)
Field write access set(FieldSignature)
Exception handler execution handler(TypeSignature)
Object initialization initialization(ConstructorSignature)
Object pre-initialization preinitialization(ConstructorSignature)
Advice execution adviceexecution()

JoinPoint 的策略的选择对应着不同 Pointcut,特别是 Pointcut 里面有着不同的 Signature。

以下有详细的说明:

Method Signature 表达式
语法

@注解 访问权限 返回值的类型 包名.函数名(参数)
例子:
@before(“execution(* android.app.Activity.on**(…))”);

  • 注解: 是可选项; 这里是 @before,关于注解的在后面 Adivce 中有更详细的说明

  • 访问权限: 可选项; 有 public, private, protected 类型;例子没有设置

  • 返回值的类型: 与普通函数的返回值类型是一样的,如果不限定类型,用通配符 * 表示。例子中是 *

  • 包名.函数名:用于查找匹配的函数,可以使用通配符

    通配符的类型

    • ’ * '表示用于匹配处 . 号之外的任意字符;
    • ’ … ’ 表示任意子 package
    • ’ + '号表示子类


    例子:

    • java.*.Data: 可以表示 java.sql.Data ,也可以表示 java.util.Date;
    • Test* : 表示Test开头的函数,可以表示 TestBase, 也可以表示 TestDervied
    • java…* : 表示 java 任意子类
    • java…*Model+: 表示 Java 任意 package 中名字以 Model 结尾的子类,比如 TabelModel, TreeModel 等
  • 函数参数
    参数有不同的型式

    • (int, char): 表示参数只有两个, 并且第一个参数是 int, 第二个参数是 char;
    • (String, …): 表示参数至少有一个。并且第一个参数是 String, 后面参数类型不限。在参数匹配中, … 代表任意参数个数和类型;
    • (Oject …): 表示不定个数的参数,并且类型都是 Object, 这里的 … 不是通配符,而是 java 中不定参数的意思;

Constructor Signature 表达式
和 Method Signature 类似
不同点:
构造函数没有返回值,并且函数名必须叫 new
例子:

public *…TestDeived.new(…)

  • public: 表示选择 public 访问权限的
  • *… : 代表任意包名
  • TestDeived.new: 代表 TestDerived 的构造函数
  • (…): 代表参数个数和类型都是任意的

Field Signature表达式
语法

@注解 访问权限 类型 类名.成员变量名

  • @注解和访问权限是可选的
  • 类型:成员变量类型, * 表示任意类型
  • 类名.成员变量名: 成员变量名可以是*, 代表任意成员变量

例子, 用 AspectJ 打印成员变量赋值前后的值

// TraceAspect.java set field 的切面
private static final String POINTCUT_FILEED =
        "set(int org.android10.viewgroupperformance.activity.MainActivity.mTest) && args(newValue) && target(t)";

@Before(POINTCUT_FILEED)
public void onFiled(JoinPoint joinPoint, Object newValue, Object t) throws IllegalAccessException {
    Object object = joinPoint.getThis();

    FieldSignature fieldSignature = (FieldSignature) joinPoint.getSignature();
    String fileName = fieldSignature.getName();
    Field field = fieldSignature.getField();
    field.setAccessible(true);
    Class clazz = fieldSignature.getFieldType();
    String clazzName = clazz.getSimpleName();

    Object oldValue = field.get(t);

    Log.i("MainActivity", "\nonFiled value = " + newValue.toString() + "\n fieldSignature =" + fieldSignature.toString()
                + "\nfield = " + field.toString() + " +  \nFileName = " + fileName
                + "\nclazzName = " + clazzName + " \noldValue = " + oldValue.toString() );
}

// 在 MainActivity.java 中
@Override
protected void onResume() {
        super.onResume();
        mTest = 100;
}

打印结果

onFiled value = 100
fieldSignature =int org.android10.viewgroupperformance.activity.MainActivity.mTest
field = private int org.android10.viewgroupperformance.activity.MainActivity.mTest +
FileName = mTest
clazzName = int
oldValue = -1

TypeSignature表达式
例子:
staticinitlization(test…TestBase): 表示 TestBase 类的 static block
handler(NullPointException): 表示 catch 到 NullPointerException 的 JPoin

2.JointPoint 间接选择

JointPoint 的直接选择是通过 Signature 信息匹配的,除此之外还有其他的方式,这些方式都可以归类到间接选择

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

当 call 捕获 joinPoint 时,捕获的签名方法的调用点;execution 捕获 joinPoint 时,捕获的则是执行点
两个的区别在于一个是 ”调用点“, 一个是 ”执行点“

对于 call 来讲

call(Before)
Pointcut {
   
    Pointcut Method
}
call(After)

对于 execution 来说

Pointcut {
   
   Execution(Before)
   Pointcut Method
   Execution(After)
}
3.AspectJ 注解的等价

AspectJ 提供了相应的注解,注解的方式和 AspectJ 语言编写是等效的。我们在 Android 中一般也是采用注解的方式

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值