博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 编译时注解-初认识
阅读量:6618 次
发布时间:2019-06-25

本文共 6458 字,大约阅读时间需要 21 分钟。

背景

编译时注解越来越多的出现在各大开源框架使用中,比如

view

事件

依赖注入

类似这样的库在开发和工作中已经越来越多,它们旨在帮助我们在效率为前提的情况下帮助开发者快速开发,节约时间成本。而它们都使用了编译时注解的思想。

正因为如此火热,所以有必要好好学习其中的实现原理,方便解决因为编译时注解导致的问题,同时可将此技术运用到自己的开源库中

思想

编译时注解框架在编写时有相对固定的格式,分包为例

这里写图片描述

格式相对固定,但是也可以灵活变动,比如讲apiannotations结合在一个moudel

moudel中的依赖关系也非常的固定

processors依赖包有api- annotations

app依赖包有 api -annotations-processors

其中除了appandroid moudel以外,其他全部均是java moudel

annotations注解

在讲解annotations注解之前,需要对java和android注解有大致的了解,可以参考我之前的博客

先初始一个HelloWordAtion注解标注Target为ElementType.TYPE修饰类对象

@Retention(RetentionPolicy.CLASS)@Target(ElementType.TYPE)public @interface HelloWordAtion {    String value();}复制代码

一般一个注解需要对应一个注解处理器,注解处理器在processors处理

processors 注解处理器

对应注解的处理器需要继承AbstractProcessor类,需要复写以下4个方法:

init

init(ProcessingEnvironment processingEnv)会被注解处理工具调用

process

process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)这相当于每个处理器的主函数main(),你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。

getSupportedAnnotationTypes

etSupportedAnnotationTypes()这里必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称

@return 注解器所支持的注解类型集合,如果没有这样的类型,则返回一个空集合

getSupportedSourceVersion

指定使用的Java版本,通常这里返回SourceVersion.latestSupported(),默认返回SourceVersion.RELEASE_6 `

@return 使用的Java版本

生成注解处理器

AbstractProcessor有了深入的了解,知道核心的初始编译时编写代码的方法及时process,在process中我们通过得到传递过来的数据,写入代码,这里先采用打印的方式,简单输出信息,后续会详细讲解如何自己实现 butterknife功能

public class HelloWordProcessor extends AbstractProcessor {    private Filer filer;    @Override    public synchronized void init(ProcessingEnvironment processingEnv) {        super.init(processingEnv);        // Filer是个接口,支持通过注解处理器创建新文件        filer = processingEnv.getFiler();    }    @Override    public boolean process(Set
annotations, RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(HelloWordAtion.class)) { if (!(element instanceof TypeElement)) { return false; } TypeElement typeElement = (TypeElement) element; String clsNmae = typeElement.getSimpleName().toString(); String msg = typeElement.getAnnotation(HelloWordAtion.class).value(); System.out.println("clsName--->"+clsNmae+" msg->"+msg); } return true; } @Override public Set
getSupportedAnnotationTypes() { Set
annotations = new LinkedHashSet<>(); annotations.add(HelloWordAtion.class.getCanonicalName()); return annotations; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); }}复制代码

到这一步HelloWordAtion对应的注解处理器已经编写完成,这里简单的打印了HelloWordAtion注解的class和注解指定的value信息

准备工作完成以后,app触发调用

@HelloWordAtion("hello")public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }}复制代码

这里注解注释的类MainActivity并且指定valuehello,到此准备工作就算完成了,这时如果你直接编译或者运行工程的话,是看不到任何输出信息的,这里还要做的一步操作是指定注解处理器的所在,需要做如下操作:

  • 1、在 processors 库的 main 目录下新建 resources 资源文件夹;

  • 2、在 resources文件夹下建立 META-INF/services 目录文件夹;

  • 3、在 META-INF/services 目录文件夹下创建 javax.annotation.process.Processors 文件;

  • 4、在 javax.annotation.process.Processors 文件写入注解处理器的全称,包括包路径;

经历了以上步骤以后方可成功运行,但是实在是太复杂了,博主为了配置这一步也是搞了好久,所以这里推荐使用开源框架AutoService

AutoService

直接在Processors中依赖

compile 'com.google.auto.service:auto-service:1.0-rc2'复制代码

使用

@AutoService(Processor.class)public class HelloWordProcessor extends AbstractProcessor {xxxxxxx}复制代码

到这里运行程序便可以成功看到后台的输出信息

这里写图片描述

需要切换到右下角的Gradle Console窗口,如果变异不成功可以clean工程以后重新运行

得到需要的数据,下一步当然是将数据写入到java class中,也就是题目所言的编译时注解,如何才能写入,这里需要借助Filer

Filer

AbstractProcessorinit方法中初始Filer

private Filer filer;      @Override      public synchronized void init(ProcessingEnvironment processingEnv) {          super.init(processingEnv);            filer = processingEnv.getFiler();      }复制代码

到此我们已经有了写入的类的帮助类,还差代码生成逻辑,这里介绍使用javapoet

javapoet

JavaPoet一个是创建 .java 源文件的辅助库,它可以很方便地帮助我们生成需要的.java 源文件,GitHub上面有非常详细的用法,建议好好阅读相关的使用

processors依赖:

compile 'com.squareup:javapoet:1.8.0'复制代码

综合上述的技术,仿照javapoet的第一个Example生成如下代码

@Override    public boolean process(Set
annotations, RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(HelloWordAtion.class)) { if (!(element instanceof TypeElement)) { return false; } TypeElement typeElement = (TypeElement) element; String clsNmae = typeElement.getSimpleName().toString(); String msg = typeElement.getAnnotation(HelloWordAtion.class).value(); System.out.println("clsName--->"+clsNmae+" msg->"+msg); // 创建main方法 MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("$T.out.println($S)", System.class, clsNmae+"-"+msg) .build(); // 创建HelloWorld类 TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main) .build(); try { // 生成 com.wzgiceman.viewinjector.HelloWorld.java JavaFile javaFile = JavaFile.builder("com.wzgiceman.viewinjector", helloWorld) .addFileComment(" This codes are generated automatically. Do not modify!") .build(); // 生成文件 javaFile.writeTo(filer); } catch (IOException e) { e.printStackTrace(); } } return true; }复制代码

这里重点讲解process方法,也就是写入代码的方法体,我们在javapoetExample基础上将输出信息改为HelloWordAtion注解获取的信息,到处便完全搞定编译时注解的整个流程,clean以后运行工程,在如下路径下便可看到自动编译生成的HelloWorld

这里写图片描述

到此简单的编译时注解就搞定了,但是编译时注解的自动写入也会导致代码混乱,可能在多次build编译过程中出现文件冲突的情况,所以这里需要引入android-apt

android-apt

android-apt能在编译时期去依赖注解处理器并进行工作,但在生成 APK 时不会包含任何遗留无用的文件,辅助 Android Studio项目的对应目录中存放注解处理器在编译期间生成的文件

依赖使用:

根目录build.gradle

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'复制代码

app

apply plugin: 'com.neenbedankt.android-apt'apt project(':processors')复制代码

这里是apt替换compile依赖processors

总结

到此简单的编译时注解就搞定了,但是api模块还没有涉及,别着急接下来的博客中继续扩展,运用掌握的编译时注解和时下主流的butterknife框架,实现一套自己的自定义注入框架中会详细讲解api模块的使用,你会发现原来butterknife很简单,当然可以自由发散,扩展回到自己的任何开源项目中,替换掉反射提高效率。迫不及待的小伙伴可以去GitHub下载源码先自行研究。


专栏


源码


建议

转载地址:http://wsupo.baihongyu.com/

你可能感兴趣的文章
Spring Security之用户名+密码登录
查看>>
java JSplitPane设置比例
查看>>
批量操作Windows域用户
查看>>
shell脚本 接受用户参数 记录一下
查看>>
健脾祛湿的中成药有哪些?
查看>>
IIS下支持下载.exe文件
查看>>
CXF WebService Hello World
查看>>
市场调研报告:企业级信息防泄漏大趋势
查看>>
济南企业短信平台的价格如何?
查看>>
requirejs
查看>>
php printf() 输出格式化的字符串
查看>>
VS2013下的64位与32位程序配置
查看>>
浅谈C中的指针和数组(二)
查看>>
SSM+Maven+IDEA增删改查
查看>>
微信小程序开发模板消息的时候 出现 errcode: 41028, errmsg: "invalid form id hint:
查看>>
2001年日语能力考试二级真题及答案
查看>>
移动端页面布局
查看>>
FUNCS.H中的函数声明
查看>>
让织梦CMS的后台编辑器支持优酷视频
查看>>
Python语言中round函数的一个疑惑
查看>>