Floating Cat

玩转注解之进阶篇

字数统计: 1.8k阅读时长: 6 min
2016/12/31 Share

编译时注解处理器

什么是Android-apt

我们知道APT是集成在javac当中的工具,这个Android-apt又是什么鬼呢? 对于从事Android开发的同学来说,ButterKnife这个开源工具可是非常熟悉.在使用该工具之前,你需要进行配置:

1
2
compile 'com.jakewharton:butterknife:8.4.0'
apt 'com.jakewharton:butterknife-compiler:8.4.0'

这里的有什么用?我们在上一节中没有配置apt插件照样可以用,这是怎么一回事?Anroid-apt是用在Android Studio中处理注解处理的插件,它有两方面的作用:

  • 只允许配置编译时注解处理器依赖,但在最终APK或者Library中不包含注解处理器的代码
  • 设置源路径,以便由注解处理器生成的代码能被Android Studio识别

另外,一些注解处理器可以接受外部的参数,这些参数在IDEA当中我们可以直接配置.但是基于IDEA而来的Android Studio反而无法直接配置,借助Android-apt插件我们可以实现该功能,其用法如下:

1
2
3
4
5
6
apt{
arguments{
配置参数名称 参数值
}

}

对与在一个jar包中的注解处理器(API和处理器)而言,我们不需要进行特殊的配置它照样可以工作,但如果我们需要在项目当中引用注解处理器生成的代码,那么就需要使用Android-apt插件来帮助解决。

对于Butter Knife/EventBus 3.0这类工具,由于需要引用注解处理器生成的代码,因此使用Android-apt.以EventBus配置为例:

b1

到现在我们恍然大悟,原来Android-apt是这么用的啊.知其然更知其所以然啊,现在无论你遇到什么样的配置问题,那都是小菜一碟.现在你正在为自己明白了Android-apt的用途的时候而感到兴奋的时候,噩耗传来:android-apt插件作者近期已经发表声明表示后续不会再继续维护该插件.what fuck?

什么是annotationProcessor

Gradle从2.2版本开始支持annotationProcessor功能来代替Android-apt.和android-apt只支持javac编译器相比,annotationProcessor同时支持javac和jack编译器.和Android-apt使用相比,annotationProcessor使用更为简单,还是以EventBus的配置为例:

b2

不难看出,使用annotationProcessor更为简单.如果你现在的Gradle版本是2.2.X以上,可以考虑替换掉Android-apt了.

项目实践

项目结构划分

由于编译时注解处理器只在编译过程中使用,因此我们不希望注解处理器相关的代码在最终的APK中存在,这样能够有效的较少方法数.比如我通常在编写注解Annotation Processor的时候会引用javapoet和Guava,如果将这些代码也打进最终的APK中会造成方法数的暴增,因此建议将注解处理器相关代码单独成为一个模块.另外为了方面注解被其他工程引用,通常我也建议将注解的定义单独划分成一个模块.综上,我们最终的项目结构如下:

  • xxx/xxx-api: 主工程/提供api,Android Library类型
  • xxx-compiler: 注解处理器模块,Java Library类型
  • xxx-annotations: 自定义注解,Java Library类型

xxx/xxx-api依赖xxx-annotations,xxx-compiler依赖xxx-annotations。这点Butter Knife给我们一个非常好的示范:

b3


替换jar为moudle依赖

基础篇中我们说道,最终要将注解处理器打成jar包,这样太麻烦了,总不能每次修改都要重新打个jar包吧,然后拷贝吧?在Android Studio当中我们同样可以采用moudle依赖的形式,这和我们以前依赖其他模块并没有太大的区别,比如我们在需要使用注解处理器的地方依赖apt即可:

1
compile project(":apt")

但要在注解处理器moudle中的build.gradle文件中配置:

1
2
sourceCompatibility = "1.7"
targetCompatibility = "1.7"

问题是moudle依赖的方式最终会导致注解处理器的代码被打包到apk中,此时我们就可以借助之前提到的Android-apt插件来避免该问题.

使用@AutoService注解简化配置

基础篇中,我们说道需要注册处理器,该过程稍显麻烦,弄不好一个粗心就写错了,有没有什么更好的方式呢?所幸Google为我们提供的@AutoService,该注解可以快速的生成META-INF/services/中注解的配置信息.

首先添加依赖:

1
compile 'com.google.auto.service:auto-service:1.0-rc2'

接下来在你的注解处理器类上使用@AutoService注解即可,如:

1
2
3
4
5
6

@AutoService(PrintProcessor.class)
public class PrintProcessor extends AbstractProcessor {

//...省略相关代码
}

和之前相比,整个配置注册处理的器的过程要简单多了,无需再关注 META-INF/services/的创建以手动配置注解处理器.关于该注解更多的信息请查看这里.

注解处理器调试

对于编译时注解处理器的调试显得略微麻烦些,不同构建方式(Maven,Ant,Gradle等)会稍微有些区别,但本质上还是离不开JPDA.编译时注解处理器运行在一个单独的JVM当中,因此想要对它进行调试可以使用Remote Debug.无论是Eclipse中还是IDEA,这两个IDE工具对Remote Debug功能都提供了良好的支持.作为IDEA二次开发出的Android Studio同样也不例外.

先来看一下如何开启JVM的远程调试功能,只需要在启动JVM的时候加上以下参数即可:

1
2
3
4
5
//jdk 1.5以前写法,当然该命令是先后兼容的
-J-Xdebug -J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005

//jdk 1.5及以后版本写法
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

这些参数的含义这里不做细说,你唯一要做的就是修改address指定一个端口号.以Android Studio中调试注解处理器为例,首先在gradle.properties中配置一下参数:

1
2
org.gradle.daemon=true
org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

接下来在控制台当中使用gradle --daemon命令来启动守护线程.

到现在为止在注解处理器工程中的配置已经完成了.接下来就需要在Android Studio中建立Remote Debugger,操作步骤如下:

b5

配置完之后是这样子的:

b6这里的端口号要保持一致,不然Remote Debugger是连不上gradle的守护线程的.设置完成后该Remote Debugger就可以成功的链接到deamon线程了.准备工作完成,下面就可以来调试了(别忘记加断点).具体怎么做呢?很简单,重新编译即可.这里为了方便演示,直接图形化操作:

b7

在构建过程中,Remote Debugger将会触发断点并挂起构建过程,接下来就可以像往常一样调试了.关于注解处理的调试就到这里,其他工具大同小议,在这里就不做说明了.

总结

有关注解开发/调试方面的问题我们已经说的差不多了.在这2016年的最后一天,我们来年再见.

CATALOG
  1. 1. 编译时注解处理器
    1. 1.1. 什么是Android-apt
    2. 1.2. 什么是annotationProcessor
  2. 2. 项目实践
    1. 2.1. 项目结构划分
    2. 2.2. 替换jar为moudle依赖
    3. 2.3. 使用@AutoService注解简化配置
  3. 3. 注解处理器调试
  4. 4. 总结