Android-AOP之AspectJ:2在studio中的使用

1,056 阅读3分钟

在Android中使用一般有两种方式,一种是通过gradle配置,一种是自定义plugin。先来看第一种。这种方式比较简单,只需要修改两个文件即可。 一,project/build.gradle中添加如下内容

buildscript {
    
    repositories {
        google()
        jcenter()
        mavenCentral()
        maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
        maven {
            url "https://maven.google.com"
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'org.aspectj:aspectjtools:1.9.0'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

二,在app/build.gradle中添加如下代码

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

第二种方式则是通过自定义gradle plugin的方式

  • 同样是新建项目AspectDemo2,然后新建Androidlibrary,取名plugin。在project模式下删除这个library下的src/main下的所有文件
  • 删除plugin/build.gradle中所有并添加如下
apply plugin: 'groovy'
apply plugin: 'maven'


dependencies {
    implementation gradleApi()
    implementation localGroovy()
    implementation"org.aspectj:aspectjtools:1.9.0"
    implementation"org.aspectj:aspectjrt:1.9.0"
}
repositories {
    mavenCentral()
}
uploadArchives {
    repositories.mavenDeployer {
       //本地仓库路径,这里是放到项目根目录下的repo文件夹
        repository(url:uri('../repo'))
       //下面这三行则是定义生成依赖包的名称为com.mwy:aspectj.demo:1.0.0
       //这个主要是在项目根目录的build.gradle文件中引用的样式(classpath'com.mwy:aspectj.demo:1.0.0')
       //groupId为组织或公司名称,artifactId为项目名或者模块儿名,version为项目或模块的当前版本号
        pom.groupId = 'com.mwy'
        pom.artifactId = "aspectj.demo"
        pom.version = '1.0.0'
    }
}
  • 在src/main目录下新建文件夹groovy,并在该文件夹中新建package,这里如com.mwy.plugin(接下来的groovy文件会在该包下)
  • 在com.mwy.plugin中新建文件AspectJPlugin.groovy
public class AspectJPlugin implements Plugin<Project>{
    @Override
    void apply(Project project) {

        final def log = project.logger

        project.dependencies {
            implementation 'org.aspectj:aspectjrt:1.9.0'
        }

        project.android.applicationVariants.all { variant ->
            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)
                ]
                MessageHandler handler = new MessageHandler(true);
                new Main().run(args, handler);

                println()
                println("####################################################################")
                println("########                                                    ########")
                println("########                                                    ########")
                println("########        This is a aspectJ demo 编译插件               ########")
                println("########                                                    ########")
                println("########                                                    ########")
                println("####################################################################")
                println()

                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;
                    }
                }
            }
        }
    }
}
  • 在src/main目录下新建目录resources/META-INF/gradle-plugins,然后在这个路径下新建一个后缀名为.properties的文件(com.mwy.aspectj.properties),注意这里的文件名会在引用该插件,即在app/build.gradle中引用(apply plugin: 'com.mwy.aspectj'),该文件内容就是声明plugin的位置
implementation-class=com.yazon.plugin.AspectJPlugin
  • 生成该jar包:studio右侧Gradle-upload-uploadArchives,成功之后可在项目的根目录下看到文件夹repo
  • 使用plugin: 在project/build.gradle中添加本地maven以及刚刚编译好的包
buildscript {
    
    repositories {
        google()
        jcenter()
        maven {
            url uri('repo')
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath'com.mwy:aspectj.demo:1.0.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

在app/build.gradle中添加引用:

apply plugin: 'com.mwy.aspectj'

还用之前测试代码进行验证:

@Aspect
public class TestAspectJ {

    @Around("execution(* *(..))")
    public void testAround(ProceedingJoinPoint joinPoint) throws Throwable {

        long startNanoTime = System.nanoTime();
        joinPoint.proceed();
        long endNanoTime = System.nanoTime();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //方法名
        String name = signature.getName();
        Log.i("tag", "name = " + name);
        Method method = signature.getMethod();
        //这里拿到方法之后还可以进行其他操作,比如拿到注解
        //method.getAnnotation(TestAnnotation.class);

        //返回值类型
        Class returnType = signature.getReturnType();
        Log.i("tag", "returnType = " + returnType.getName());

        //方法所在类名
        Class declaringType = signature.getDeclaringType();
        Log.i("tag", "declaringType = " + declaringType.getCanonicalName());

        //参数类型
        Class[] parameterTypes = signature.getParameterTypes();
        for (Class cls : parameterTypes) {
            Log.i("tag", " cls = " + cls.getSimpleName());
        }

        //参数名
        String[] parameterNames = signature.getParameterNames();
        for (String param : parameterNames) {
            Log.i("tag", " param = " + param);
        }

        Log.i("tag", String.valueOf(endNanoTime - startNanoTime));
    }
}

上面定义的切点是在所有方法中插入代码,而MainActivity中的代码为

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i("tag", "onCreate execute complete");
    }
}

实际运行后的打点log显示为