android- AspectJ 的接入使用

 

AspectJ 作为aop的著名工具,现在就让我们进行使用他,不逼逼。

接入有两种方式,一种是组件化式接入,一种是非组件化接入。组件化接入,就是当我们的项目是组件化开发时,有多个moudle。

非组件化接入:

第一步:在项目gradle中添加aspectJ的依赖

classpath 'org.aspectj:aspectjtools:1.8.6'

第二步:在app gradle中添加依赖和配置

implementation 'org.aspectj:aspectjrt:1.8.13'
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

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;
            }
        }
    }
}

 

以上代码放到最下面就好了。

现在配置就完成了

第三步:添加测试:

**
 * Created by malei on 2019/2/28.
 */
@Aspect   //必须使用@AspectJ标注,这样class DemoAspect就等同于 aspect DemoAspect了
public class DemoAspect {
     String TAG = "DemoAspect";
    /*
    @Pointcut:pointcut也变成了一个注解,这个注解是针对一个函数的,比如此处的logForActivity()
    其实它代表了这个pointcut的名字。如果是带参数的pointcut,则把参数类型和名字放到
    代表pointcut名字的logForActivity中,然后在@Pointcut注解中使用参数名。
    基本和以前一样,只是写起来比较奇特一点。后面我们会介绍带参数的例子
    */
    @Pointcut("execution(* com.ml.maskpro.ui.MainActivity.onCreate(..)) ||"
            +"execution(* com.ml.maskpro.ui.MainActivity.onStart(..))")
    public void logForActivity(){};  //注意,这个函数必须要有实现,否则Java编译器会报错

    /*
    @Before:这就是Before的advice,对于after,after -returning,和after-throwing。对于的注解格式为
    @After,@AfterReturning,@AfterThrowing。Before后面跟的是pointcut名字,然后其代码块由一个函数来实现。比如此处的log。
    */
    @Before("logForActivity()")
    public void log(JoinPoint joinPoint){
        //对于使用Annotation的AspectJ而言,JoinPoint就不能直接在代码里得到多了,而需要通过
        //参数传递进来。
        Log.e(TAG, joinPoint.toShortString() + " aop拦截");
    }
}

这样,我们的MainActivity的onCreate()方法被触发的时候,我们就会进行拦截,并打印指定的内容。

组件化接入

使用组件化的方式接入,就避免了重复编写代码的问题。

接下来,用几步就完成我们的组件化接俄入吧。

第一步:首先让我们创建一个moudle -->  aspectJlib 

1)moudle 里面的东西都删除(除了build.gradle,但是要被gradle里面的所有内容删除)

2)在aspectJlib里面新增一个“src”文件夹,往src文件夹新增main文件夹,再往main文件夹新增groovy和resources文件夹

3)resources目录下新建文件夹META-INF,META-INF文件夹下新建gradle-plugins文件夹。

4)在groovy目录下新建项目包名,就像Java包名那样。

android- AspectJ 的接入使用_第1张图片

第二步:打开Module下的build.gradle文件,输入

apply plugin: 'groovy'
apply plugin: 'maven'

dependencies {
    implementation gradleApi()//gradle sdk
    implementation localGroovy()//groovy sdk
    implementation 'com.android.tools.build:gradle:3.0.1'
    implementation 'org.javassist:javassist:3.20.0-GA'
    implementation 'org.aspectj:aspectjtools:1.8.13'
}

//上传到仓库
uploadArchives {
    repositories.mavenDeployer {
        pom.groupId = 'com.ml.pro'
        pom.artifactId = 'aspectjlib'
        pom.version = '1.0.0'

        repository(url: uri('../repo'))
    }
}

repositories {
    jcenter()
}

uploadArchives闭包里面配置的是将插件发布到本地仓库。然后maven本地仓库的目录就是当前项目目录下的repo目录。

第三步:在groovy下的包名下新建一个文件,命名为AspectjPlugin.groovy

package com.ml.pro

import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
import org.gradle.api.Plugin
import org.gradle.api.Project

/**
 * @使用ajc编译java代码 , 同 时 织 入 切 片 代 码
 * 使用 AspectJ 的编译器(ajc,一个java编译器的扩展)
 * 对所有受 aspect 影响的类进行织入。
 * 在 gradle 的编译 task 中增加额外配置,使之能正确编译运行。
 */
public class AspectjPlugin implements Plugin {

    void apply(Project project) {
        project.dependencies {
            compile 'org.aspectj:aspectjrt:1.8.9'
        }
        final def log = project.logger
        log.error "========================";
        log.error "Aspectj切片开始编织Class!";
        log.error "========================";
        project.android.applicationVariants.all { variant ->
            def 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;
                    }
                }
            }
        }
    }
}

第四步:在resources/META-INF/gradle-plugins目录下新建一个 AspectjPlugin.properties 文件,命名就是刚才创建 .groovy 的名字.

在里面输入:

implementation-class=com.ml.pro.AspectjPlugin

第五步:右侧的gradle Toolbar就会在module下多出一个task,点击uploadArchives这个Task,就会在项目下多出一个repo目录,里面存着这个gradle插件。

这样切面组件的组件化部分就完成了。现在就可以提供给其他module使用了。

第六步:在项目 gadle中添加

buildscript {

    repositories {
        ...
        maven { url uri('repo') }  //插件仓库路径

    }
    dependencies {
        ...
        classpath 'com.ml.pro:aspectjlib:1.0.0' //插件
    }
}

第七步:先Rebuild Project,最后在app下的build.gradle添加配置

apply plugin: 'AspectjPlugin'


同时也要添加依赖(但是在实际编码时,没有添加依然可以使用):
compile 'org.aspectj:aspectjrt:1.8.9'

这样就完成了,我们就进行测试:

/**
 * Created by malei on 2019/2/28.
 */
@Aspect
public class AspectTest {
    final String TAG = AspectTest.class.getSimpleName();

    /**
     * 在MainActivity的所有生命周期的方法中打印log
     * @param joinPoint
     * @throws Throwable
     */
    @Before("execution(* *..MainActivity+.on**(..))")
    public void method(JoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String className = joinPoint.getThis().getClass().getSimpleName();

        Log.e(TAG, "class:" + className);
        Log.e(TAG, "method:" + methodSignature.getName());
    }

}

 

你可能感兴趣的:(android)