在Android项目中使用AspectJ的方法

作者:Cavalier- 时间:2023-02-01 23:33:54 

什么是AOP

AOP是 Aspect Oriented Programming 的缩写,即面向切面编程,和平常遇到的面向对象OOP编程不一样的是,OOP是将功能模块化对象化,AOP是针对同一类的问题统一化处理。例如做日志埋点,性能监控,动态权限控制等。

AspectJ

AspectJ实际上是对AOP编程的实践,目前还有很多的AOP实现,如ASMDex,但笔者选用的是AspectJ。

在Android项目中使用AspectJ

如果使用原生AspectJ在项目中配置会非常麻烦,在GitHub上有个开源的SDK gradle_plugin_android_aspectjx基于gradle配置即可。

接入说明

请自行查看开源项目中的接入配置过程

AspectJ 之 Join Points介绍

Join Points在AspectJ中是关键的概念。Join Points可以看做是程序运行时的一个执行点,比如:一个函数的调用可以看做是个Join Points,相当于代码切入点。但在AspectJ中,只有下面几种执行点是认为是Join Points:

Join Points说明实例
method call函数调用比如调用Log.e(),这是一个个Join Point
method execution函数执行比如Log.e()的执行内部,是一处Join Points。注意这里是函数内部
constructor call构造函数调用和method call 类似
constructor execution构造函数执行和method execution 类似
field get获取某个变量比如读取DemoActivity.debug成员
field set设置某个变量比如设置DemoActivity.debug成员
pre-initializationObject在构造函数中做的一些工作。-
initializationObject在构造函数中做的工作。-
static initialization类初始化比如类的static{}
handler异常处理比如try catch 中,对应catch内的执行
advice execution这个是AspectJ 的内容-

Pointcuts 介绍

一个程序会有多个Join Points,即使同一个函数,也还分为call 和 execution 类型的Join Points,但并不是所有的Join Points 都是我们关心的,Pointcuts 就是提供一种使得开发者能够值选择所需的JoinPoints的方法。

Advice

Advice就是我们插入的代码可以以何种方式插入,有Before 还有 After、Around。
下面看个例子:


@Before(“execution(* android.app.Activity.on**(..)))”)
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable{
}

这里会分成好几个部分,我们依次来看:

  1. @Before: Advice, 也就是具体的插入点

  2. execution:处理Join Point的类型,例如call、execution

  3. (* android.app.Activity.on**(..)): 这个是最重要的表达式,第一个*表示返回值,*表示返回值为任意类型,后面这个就是典型的包名路径,其中可以包含 *来进行通配,几个 *没有区别。同时这里可以通过&&、||、!来进行条件组合。()代表这个方法的参数,你可以指定类型,例如android.os.Bundle,或者 (..) 这样来代表任意类型、任意个数的参数。

  4. public void onActivityMehodBefore: 实际切入的代码。

Before 和 After 其实还是很好理解的,也就是在Pointcuts之前和之后,插入代码,那么Android呢,从字面含义上来讲,也就是在方法前后各插入代码,他包含了 Before和 After 的全部功能,代码如下:


@(“execution(* com.xys.aspectjxdemo.MainActivity.testAOP()))”)
public void onActivityMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
String key = proceedingJoinPoint.getSignature().toString();
Log.d(TAG,”onActivityMethodAroundFirst:”+key);
proceedingJoinPoint.proceed();
Log.d(TAG,”onActivityMethodAroundSecond:”+key);
}

以上代码中,proceedingJoinPoint.proceed()代表执行原始的方法,在这之前、之后,都可以进行各种逻辑处理。

自定义Pointcuts

自定义Pointcuts可以让我们更加精准的切入一个或多个指定的切入点。

首先我们要定义一个注解类


@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface DebugTrace {
}

在需要插入代码的地方加入这个注解,例如在MainActivity中加入:


public class MainActivity extends AppCompatActivity{
final String TAG = MainActivity.class.getSimpleName();

@Override
protedcted void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
logTest();
}

@DebugTrace
public void logTest(){
Log.e(TAG,”log test");
}
}

最后创建切入代码


@Pointcut(“execution(@com.kun.aspectjtest.aspect.DebugTrace * *..*.*(..))”)
public void DebugTraceMethod(){}

@Before(“DebugTraceMethod()”)
public void beforeDebugTraceMethod(JoinPoint joinPoint) throws Throwable{
String key = joinPoint.getSignature().toString();
Log.e(TAG, “beforeDebugTraceMethod:”+key);
}

Call

在AspectJ的切入点表达式中,我们前面都是使用的execution,实际上还有一种类型—call,那么这两种语法有什么区别呢?对call来说:


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

对Execution来说:


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

Withincode

这个语法通常来进行一些切入点条件的过滤,作更加精确的切入控制,如下:


public class MainActivity extends AppCompatActivity{
final String TAG = MainActivity.class.getSimpleName();

@Orveride
protected void onCreate(Bundle savedInstanceState){
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
aspectJ1();
aspectJ2();
aspectJ3();
}
public void aspectJTest(){
Log.e(TAG,”execute aspectJTest");
}

public void aspectJ1(){
aspectJTest();
}
public void aspectJ2(){
aspectJTest();

}
public void aspectJ3(){
aspectJTest();
}
}

aspectJ1(),aspectJ2(),aspectJ3()都调用了aspectJTest方法,但只想在aspectJ2调用aspectJTest时插入代码,这个时候就需要使用到Pointcut和withcode组合的方式,来精确定位切入点。


@Pointcut(“(call(* *..aspectJTest()))&&withincode(* *..aspectJ2())”)
public void invokeAspectJTestInAspectJ2(){
}

@Before(“invokeAspectJTestInAspectJ2()”)
public void beforeInvokeaspectJTestInAspectJ2(JoinPoint joinPoint) throws Throwable{
Log.e(TAG,”method:”+getMethodName(joinPoint).getName());
}

private MethodSignature getMethodName(JoinPoint joinPoint){
if(joinPoint == null) return null;
return (MethodSignature) joinPoint.getSignature();
}

execution 语法

execution()是最常用的切点函数,其语法如下所示:

例如下面这段语法:@Around(“execution(* *..MainActivity+.on*(..))")

整个表达式可以分为五个部分:
1.execution()是表达式主体
2.第一个*号代表返回类型,*号代表所有的类型。
3.包名 表示需要拦截的包名,这里使用*.代表匹配所有的包名。
4.第二个*号表示类名,后面跟.MainActivity是指具体的类名叫MainActivity。
5.*(..) 最后这个星号表示方法名,+.代表具体的函数名,*号通配符,包括括弧号里面表示方法的参数,两个dot代表任意参数。

遇到的错误

1.以下错误可以使用gradle2.2.3解决,由于目前还不适配gradle3.0导致的

Error:Execution failed for task ':app:transformClassesWithDexBuilderForDebug'.
> Unexpected scopes found in folder '/Users/ram/WorkSpace/AndroidWorkSpace/MyDemo/app/build/intermediates/transforms/AspectTransform/debug'. Required: PROJECT, SUB_PROJECTS, EXTERNAL_LIBRARIES. Found: EXTERNAL_LIBRARIES, PROJECT, PROJECT_LOCAL_DEPS, SUB_PROJECTS, SUB_PROJECTS_LOCAL_DEPS

来源:https://www.cnblogs.com/cavalier-/p/8888459.html

标签:Android,AspectJ
0
投稿

猜你喜欢

  • C# 设置防火墙的创建规则

    2023-03-21 09:13:42
  • Java设计模式之模版方法模式简介

    2023-07-16 14:58:32
  • Java开发人员需知的十大戒律

    2023-09-17 07:33:50
  • 深入理解Java责任链模式实现灵活的请求处理流程

    2022-06-06 23:33:53
  • Kotlin Flow数据流的3种使用场景详解

    2021-06-03 09:06:18
  • SpringBoot通过源码探究静态资源的映射规则实现

    2022-03-26 19:05:53
  • 如何利用C#正则表达式判断是否是有效的文件及文件夹路径

    2022-03-06 23:34:45
  • Spring Security登录表单配置示例详解

    2023-10-12 09:03:55
  • SpringSecurity Jwt Token 自动刷新的实现

    2022-04-28 18:49:45
  • C# 定时器定时更新的简单实例

    2023-01-08 12:45:50
  • Guava - 并行编程Futures详解

    2022-04-28 23:16:34
  • Android P实现静默安装的方法示例(官方Demo)

    2022-04-05 20:06:13
  • javaweb学习总结——使用JDBC处理MySQL大数据

    2022-10-19 22:45:32
  • 详解JAVA 内存管理

    2023-01-13 04:35:51
  • Java实现从Html文本中提取纯文本的方法

    2023-06-18 20:28:26
  • Android ViewPager实现无限循环轮播广告位Banner效果

    2023-06-26 22:02:29
  • 浅析SpringBoot2.4 静态资源加载问题

    2023-01-29 11:04:33
  • Idea如何导入一个SpringBoot项目的方法(图文教程)

    2022-08-10 22:40:49
  • java 生成xml并转为字符串的方法

    2023-01-07 08:27:30
  • java序列化和java反序列化示例

    2023-11-24 04:01:03
  • asp之家 软件编程 m.aspxhome.com