Android 引入 AspectJ进行AOP开发的记录。
在不试用插件时,引入AspectJ到android工程
1. 在项目gradle引入插件:
dependencies {
...
classpath 'org.aspectj:aspectjtools:1.9.5'
...
}
2. 在创建aspect文件的包引入
dependencies {
...
implementation 'org.aspectj:aspectjrt:1.9.5'
...
}
并在同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 ->
JavaCompile javaCompile
if (variant.hasProperty('javaCompileProvider')) {
//android gradle 3.3.0 +
javaCompile = variant.javaCompileProvider.get()
} else {
javaCompile = variant.javaCompile
}
def buildType = variant.buildType.name
javaCompile.doLast {
MessageHandler handler = new MessageHandler(true)
String[] javaArgs = [
"-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)
]
new Main().run(javaArgs, handler)
String[] kotlinArgs = [
"-showWeaveInfo",
"-1.8",
"-inpath", project.buildDir.path + "/tmp/kotlin-classes/" + buildType,
"-aspectpath", javaCompile.classpath.asPath,
"-d", project.buildDir.path + "/tmp/kotlin-classes/" + buildType,
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)
]
new Main().run(kotlinArgs, 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:
case IMessage.INFO:
log.info message.message, message.thrown
break
case IMessage.DEBUG:
log.debug message.message, message.thrown
break
}
}
}
}
这样就成功引入AspectJ工具了,下一步编写Aspect文件。
3. 先实现自己的业务逻辑
package com.example.cheng.test;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.widget.TextView;
import com.example.common.DisplayMetricsUtil;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivityJava extends AppCompatActivity {
@SuppressLint("SetTextI18n")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView text = findViewById(R.id.text);
text.measure(0, 0);
text.setText("text width: " + DisplayMetricsUtil.px2dp(this, text.getMeasuredWidth()));
}
}
4. 再在同module下实现aspect文件
package com.example.cheng.test;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class MainActivityAspectJava extends BaseAspect {
@Pointcut("execution(* com.example.cheng.test.MainActivityJava.on*(..))")
public void mainPointCut() {
}
@After("mainPointCut()")
public void hookMain(JoinPoint joinPoint) throws Throwable {
log("java::" + joinPoint.getSignature().toLongString());
}
@Pointcut("call(* com.example.common.DisplayMetricsUtil.*(..))")
public void displayPointCut() {
}
@After("displayPointCut()")
public void hookDisplay(JoinPoint joinPoint) throws Throwable {
log("java::" + joinPoint.getSignature().toLongString());
}
}
输出效果:
补充记录:
1. 因为kotlin文件编译出的.class文件路径与java不同,所以需要额外为aspect tool指定kotlin的路径。
String[] kotlinArgs = [
"-showWeaveInfo",
"-1.8",
"-inpath", project.buildDir.path + "/tmp/kotlin-classes/" + buildType,
"-aspectpath", javaCompile.classpath.asPath,
"-d", project.buildDir.path + "/tmp/kotlin-classes/" + buildType,
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)
]
new Main().run(kotlinArgs, handler)
待解决问题:
1. kotlin每次都需要clean build
2. 如何跨module实现aop weave,即在一个包中,可以通过pointcut切入其他module中的方法及其调用。
3. Java写的业务逻辑,kotlin的aspect文件能够对其织入;Kotlin写的业务逻辑,Java的Aspect文件不能对其织入。