我的需求是使用注解实现对一个方法计算运行时的耗时,希望效果如下:
public class MainActivity extends AppCompatActivity {
@Override
@Trace(pkg = "test")
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
05-25 11:03:34.939 7192-7192/org.cmdmac.aoptest W/TraceAspect: test.onCreate cost 54.054038ms
在onCreate中定义@Trace注解,运行有上面的Log。
AOP原理这里不具体阐述,来说说工程依赖关系。
首先有个只p定义了Trace注解的Library工程名为annonation,然后有个AOP实现的Library工程aop,还有个使用注解的工程app.
根据AOP实现原理,需要对aop和app两个工程使用aspectj对生成的class进行编译,如果不使用gradle插件,则需要在aop下面的build.gralde使用如下脚本:
final def variants = project.android.libraryVariants
JavaCompile javaCompile = variant.javaCompile
//编译完后使用aspectj来编译
javaCompile.doLast {
String[] args = [
"-showWeaveInfo",
"-1.5",
"-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;
//other case
}
}
}
还要在app的build.gradle使用如下脚本:
//library工程使用libraryVariants,app工程使用applicationVariants
final def variants = project.android.applicationVariants
JavaCompile javaCompile = variant.javaCompile
//跟上面一样,此处省略一万字
可以看到上面两个build.gradle文件中使用了重复的逻辑,占用了很大的篇幅,实际只有获取variants时不一样,因此如果能用gradle插件实现这段逻辑,使用时应用这个插件,build.gradle文件会清爽很多,以后也只要维护插件代码即可。简而言之最终想达到的效果就是只在build.gradle文件引用插件代替上面一大段:
apply plugin: 'org.cmdmac.aop'
那如何实现这么一个插件呢?
1. 首先开发gradle插件需要使用AndroidStudio新建一个library工程名为aopplugin,把src/main下的java目录重命名为groovy(使用groovy语言来开发插件)、res目录重命名为resources,接下来删掉resources目录下的所有目录和文件并把目录结构调整为如下:
META-INF //目录
gradle-plugins //目录
org.cmdmac.aop.properties //插件属性文件
org.cmdmac.aop.properties是插件的配置文件,注意!org.cmdmac.aop就是apply plugin时的插件名
2. 做好这些工作后开始写你的插件代码吧,在groovy目录下建立自己的package和groovy插件文件,比如,我这里是用的包名是org.cmdmac.aopplugin下面的groovy文件是HugoPlugin.groovy,在这个文件中实现插件逻辑:
//gradle插件要实现Plugin接口,void apply(Project)数就是调用apply plugin 'xxx'时会调用的方法
class HugoPlugin implements Plugin {
@Override void apply(Project project) {
def hasApp = project.plugins.withType(AppPlugin)
def hasLib = project.plugins.withType(LibraryPlugin)
if (!hasApp && !hasLib) {
throw new IllegalStateException("'android' or 'android-library' plugin required.")
}
final def log = project.logger
final def variants
//根据工程类型判断使用不同的variant,这样在不同的工程apply时就会使用不同的variant啦
if (hasApp) {
variants = project.android.applicationVariants
} else {
variants = project.android.libraryVariants
}
JavaCompile javaCompile = variant.javaCompile
//跟前面不用插件形式的代码一样,复制过来就可以了,此处省略
}
}
3. 实现了插件逻辑后再在org.cmdmac.aop.properties文件中指定插件的实现类,其实就是入口类
implementation-class=org.cmdmac.aopplugin.HugoPlugin
4. 好了,你现在已经写好插件了,但还没编译和生成,还不能被其他工程使用,接下来需要配置插件工程aopplugin的build.gradle文件
apply plugin: 'groovy'
apply plugin: 'maven'
dependencies {
//指定编译插件需要的依赖
implementation gradleApi()
implementation localGroovy()
implementation 'com.android.tools.build:gradle:3.1.2'
//aspectj需要到的类
implementation 'org.aspectj:aspectjtools:1.8.5'
}
//配置group和version;gradle插件的命名方式:group:artifactId:version我们这里会是org.cmdmac.aop.aoplugin.1.0.0,插件名artifactId默认是使用插件工程名的
group='org.cmdmac.aop'
version='1.0.0'
//生成插件的task
uploadArchives {
repositories {
mavenDeployer {
//我们发布到本地的地址,可以发布到jcenter上,具体可google下相关教程
repository(url: uri('../repo'))
}
}
}
5. 写好之后就可以执行uploadArchives任务就可以在与aopplugin目录平级有个repo目录下找到插件生成的文件。
到这里就可以在工程里引用插件来开发了,首先在Project级的build.gradle引入插件地址:
buildscript {
repositories {
jcenter()
google()
mavenLocal()
maven {
//使用本地仓库,即插件目录地址
url "/home/fengzhiping/work/android/AOP4Android/repo"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
//引用插件
classpath 'org.cmdmac.aop:aopplugin:1.0.0'
}
}
然后在aop和app目录下的gradle应用插件
apply plugin: 'org.cmdmac.aop'
最后配置好app、aop、annotation依赖关系,在app工程使用一开始使用的文件构建apk运行就能看到onCreate执行时间了。
上面例子在github https://github.com/Cmdmac/AOPAndroid