Android面向切面编程框架(AspectJ 讲解)

安装AspectJ

Android上的ApsectJ开发由几部分组成,AspectJ gradle插件,ApsectJ依赖,还有 AspectJ编译器。
首先安装AspectJ编译器很简单,就跟安装JAVA环境一样,
下载链接:http://www.eclipse.org/downloads/download.php?file=/tools/aspectj/aspectj-1.9.0.jar
目前最新的已经更新到1.9.1了。如果你电脑已经有JAVA环境的话直接运行这个jar包就行,
在安装完毕后需要配置环境变量到 aspectj的bin目录下,这里不赘述

export PATH="$PATH:~/Library/Android/sdk/platform-tools"
export PATH="$PATH:/usr/local/opt/gradle/gradle-4.1/bin"
export PATH="$PATH:~/Library/Android/sdk/ndk-bundle"
export PATH="$PATH:~/Library/flutter/bin"
export PATH="$PATH:~/Library/kotlinc/bin"
export PATH="$PATH:~/Library/AspectJ/bin" <- AspectJ的PATH
配置完后运行 ajc -v 应该可以看到对应输出

AspectJ Compiler 1.9.1 (1.9.1 - Built:  2018 at 17:52:10 GMT)


配置Android Gradle增加AspectJ依赖

首先需要把 AspectJ 依赖加到 gradle根目录中,

buildscript {
    repositories {
        maven { url 'https://maven.google.com' }
        maven { url 'https://jitpack.io' }
        jcenter()
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'org.aspectj:aspectjtools:1.9.1'
        classpath 'org.aspectj:aspectjweaver:1.9.1'
    }
}

然后在项目app目录的build.gradle需要添加以下内容,

import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

apply plugin: 'com.android.application'


android {

    ...

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'org.aspectj:aspectjrt:1.9.1'
}

final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
    if (!variant.buildType.isDebuggable()) {
        log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
        return
    }

    JavaCompile javaCompile = variant.javaCompile
    javaCompile.doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.8",
                         "-inpath", javaCompile.destinationDir.toString(),
                         "-aspectpath", javaCompile.classpath.asPath,
                         "-d", javaCompile.destinationDir.toString(),
                         "-classpath", javaCompile.classpath.asPath,
                         "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
        log.debug "ajc args: " + Arrays.toString(args)

        MessageHandler handler = new MessageHandler(true)
        new Main().run(args, handler)
        for (IMessage message : handler.getMessages(null, true)) {
            switch (message.getKind()) {
                case IMessage.ABORT:
                case IMessage.ERROR:
                case IMessage.FAIL:
                    log.error message.message, message.thrown
                    break
                case IMessage.WARNING:
                    log.warn message.message, message.thrown
                    break
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break
            }
        }
    }
}

创建AspectJ代码

下面这部分代码看起来会一脸懵逼,不过目前先不用管具体的语法含义,
先跑起来环境,然后再结合理论慢慢在修改代码中感受就能快速的上手AOP了。
以一个HelloWorld为例子,我们的MainActivity中啥事情不干,只有基本的生命周期方法。

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onStart() {
        super.onStart();
    }
}

现在我们要写一个AspectJ类。
这个类要做的事情是告诉ACJ编译器,要在MainActivity中的每个方法前面打印一行log,输出当前执行的是哪个方法,

@Aspect
public class AspectTest {

    @Pointcut("execution(* phoenix.com.helloaspectj.MainActivity.**(..))")
    public void executeAspectJ() {
    } // 注意*号后面必须有空格

    @Before("executeAspectJ()")
    public void beforeAspectJ(JoinPoint joinPoint) throws Throwable  {
        Log.d(”tag“, "injected -> " + joinPoint.toShortString());
    }
}

第一次接触AspectJ的看到这段代码有点摸不着头脑,解释一下几个注解的意思,
@Aspect: 告诉ACJ编译器这是个AspectJ类
@PointCut: PointCut是AspectJ中的一个概念,跟它一起的另一个概念是 JoinPoint,这两个概念一起描述要注入的切面
@Before: 表示要注入的位置,常用的有 Before/After/Around,分别表示在执行前,执行后,和取代原方法

这里@PointCut注解后的参数表示的意思是对 MainActivity中的所有方法进行注入,参数用的是正则匹配语法。
下面看看这段代码执行的结果

16:04:56.611 22823-22823/? D/tag: injected -> execution(MainActivity.onCreate(..))
16:04:56.661 22823-22823/? D/tag: injected -> execution(MainActivity.onStart())

看到虽然我们没有在MainActivity中写入log打印语句,但是通过AspectJ实现了,在MainActivity两个生命周期执行前插入了我们自己的log。

严格的说使用方式有两种,随便网上搜一下都能找到,例子就不多举了,随便体验一个简单,有了直接的感受就好。

本篇主要目的还是让新手快速上手并亲身体会,关键 让你知道我们开发的时候用AspectJ来干点啥。。

(知识点很简单,主要还是消除大家心里的疑惑,带着疑惑很难接受新知识)---这也是我的一个学习方法。


Pointcuts 示例

以下示例表示在aspectjx插件下,相同包是指同一个aar/jar包,AspectJ常规配置下不同包不能执行“execution”织入

execution

    execution(* com.howtodoinjava.EmployeeManager.*( .. ))
    匹配EmployeeManger接口中所有的方法
    execution(* EmployeeManager.*( .. ))
    当切面方法和EmployeeManager接口在相同的包下时,匹配EmployeeManger接口中所有的方法
    execution(public * EmployeeManager.*(..))
    当切面方法和EmployeeManager接口在相同的包下时,匹配EmployeeManager接口的所有public方法
    execution(public EmployeeDTO EmployeeManager.*(..))
    匹配EmployeeManager接口中权限为public并返回类型为EmployeeDTO的所有方法。
    execution(public EmployeeDTO EmployeeManager.*(EmployeeDTO, ..))
    匹配EmployeeManager接口中权限为public并返回类型为EmployeeDTO,第一个参数为EmployeeDTO类型的所有方法。
    execution(public EmployeeDTO EmployeeManager.*(EmployeeDTO, Integer))
    匹配EmployeeManager接口中权限为public、返回类型为EmployeeDTO,参数定义为EmployeeDTO,Integer的所有方法。
    "execution(@com.xyz.service.BehaviorTrace * *(..))"
    匹配注解为"@com.xyz.service.BehaviorTrace",返回值为任意类型,任意包名下的任意方法。

within

任意连接点:包括类/对象初始化块,field,方法,构造器

    within(com.xyz.service.*)
    com.xyz.service包下任意连接点
    within(com.xyz.service..*)
    com.xyz.service包或子包下任意连接点
    within(TestAspect)
    TestAspect类下的任意连接点
    within(@com.xyz.service.BehavioClass *)
    持有com.xyz.service.BehavioClass注解的任意连接点

主要应用场景:(重点)

  • 数据统计
  • 日志记录
  • 用户行为统计
  • 应用性能统计
  • 数据校验
  • 行为拦截
  • 无侵入的在宿主中插入一些代码,
  • 做日志埋点
  • 性能监控
  • 动态权限控制
  • 代码调试

AspectJ 只是 AOP 的一种手段,类似的还有用 asm 去修改字节码。

AOP之所以会有越来越多的人去了解。

  • 第一,非常好的去耦合。
  • 第二,可以用AOP来实现无痕埋点,数据收集,甚至修改SDK中动不了的代码。

暂不支持 Java 9 以上平台

其他问题请看插件的文档和 Issues。


熬夜写教程不容易,
如果你欣赏我的代码,
可以赞赏我几块钱买个新键盘。

发布了68 篇原创文章 · 获赞 128 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/gfg156196/article/details/85265331
今日推荐