Apt详解(一)

简介

APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。
这里面有几个关键字:处理注解,编译生成,我们总结一句话就是APT能够在编译期通过处理注解,生成我们想要的文件。

注解处理器是 javac 自带的一个工具,用来在编译时期扫描处理注解信息。你可以为某些注解注册自己的注解处理器。这里,我假设你已经了解什么是注解及如何自定义注解。如果你还未了解注解的话,可以查看官方文档。注解处理器在 Java 5 的时候就已经存在了,但直到 Java 6 (发布于2006看十二月)的时候才有可用的API。过了一段时间java的使用者们才意识到注解处理器的强大。所以最近几年它才开始流行。

一个特定注解的处理器以 java 源代码(或者已编译的字节码)作为输入,然后生成一些文件(通常是.java文件)作为输出。那意味着什么呢?你可以生成 java 代码!这些 java 代码在生成的.java文件中。因此你不能改变已经存在的java类,例如添加一个方法。这些生成的 java 文件跟其他手动编写的 java 源代码一样,将会被 javac 编译

用法

工程目录结构如下:


图片.png

annotation 是我们存放注解的地方。
compiler 使我们处理注解的地方。
api 是我们提供对外调用的api。
现在有这个需求只要在我们的类上加了@Test的注解就会生成

package com.delta.aptlearning;
import java.lang.String;
import java.lang.System;
public class MainActivity$$helloWorld {
  public static void main(String[] args) {
    System.out.println("app");
  }
}
  1. 创建注解libary annotation里面注解如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Test {
}

我们@target是Type表明该注解只能注解类获接口

  1. 创建compiler libary,因为我们的AbstractProcesso这个类是属于java不需要android一些插件所以我们可以创建javalibary。buildgradle如下:
apply plugin: 'java'
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.google.auto.service:auto-service:1.0-rc2'
    compile 'com.squareup:javapoet:1.7.0'
    compile project(':annotation')
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
  • 定义编译版本jdk为1.7
  • AutoService主要的作用是注解processor类,并对其生成META—INF的配置信息(这里可以不用这个,也可以按照原始方式进行注解)
  • javapoet主要的作用是帮助我们通过类调用的形式生成代码(也可以用string类型的方式进行拼接)
  • annotaion是我们要依赖的使用的注解库
  1. 处理注解。这时候我们要用到abstractProcessor类,我们看下api
@Override
    public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return super.getSupportedSourceVersion();
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        return false;
    }
  • init(ProcessingEnvironment processingEnv) :所有的注解处理器类都必须有一个无参构造函数。然而,有一个特殊的方法init(),它会被注解处理工具调用,以ProcessingEnvironment作为参数。ProcessingEnvironment 提供了一些实用的工具类Elements, Types和Filer。我们在后面将会使用到它们。

  • process(Set<? extends TypeElement> annoations, RoundEnvironment env) :这类似于每个处理器的main()方法。你可以在这个方法里面编码实现扫描,处理注解,生成 java 文件。使用RoundEnvironment 参数,你可以查询被特定注解标注的元素(原文:you can query for elements annotated with a certain annotation )。

  • getSupportedAnnotationTypes():在这个方法里面你必须指定哪些注解应该被注解处理器注册。注意,它的返回值是一个String集合,包含了你的注解处理器想要处理的注解类型的全称。换句话说,你在这里定义你的注解处理器要处理哪些注解。注意这里也可以用注解的方式来实现

    eg: @SupportedAnnotationTypes("com.delta.annotationmodule.Test")

  • etSupportedSourceVersion() : 用来指定你使用的 java 版本,注意此处也可以用注解的方式来实现
    eg:@SupportedSourceVersion(SourceVersion.RELEASE_6)

==这个内容会抽出一片文章详细介绍==

  1. 注册注解
    你可能会问 “怎样注册我的注解处理器到 javac ?”。你必须提供一个.jar文件。就像其他 .jar 文件一样,你将你已经编译好的注解处理器打包到此文件中。并且,在你的 .jar 文件中,你必须打包一个特殊的文件javax.annotation.processing.Processor到META-INF/services目录下

    第一种方案

image
  • 在main文件夹下创建resources文件夹

  • 在resources资源文件夹下创建META-INF文件夹

  • 然后在META-INF文件夹中创建services文件夹

  • 然后在services文件夹下创建名为javax.annotation.processing.Processor的文件,在该文件中配置需要启用的注解处理器,即写上处理器的完整路径,有几个处理器就写几个,分行写幺,比如我们这里是:com.example.TestProcessor

    第二种方案

  • 在buildGradle文件中我们要加入 compile 'com.google.auto.service:auto-service:1.0-rc2'

  • 在我们的注解处理器上加上

    @AutoService(Processor.class)
    public class TestProcessor extends AbstractProcessor 
    

app用法

最终我们生成的注解需要在我们的android工程里面去应用怎么做呢?
这时候要用到Android-apt,那么问题来来了什么是android apt?
首先我们先看几个问题?

1. 首先注解处理器也就是我们的compile不应该打包到我们的apk中增加体积。
2. 生成的文件怎么被android studio引用
3. 怎么从buildgradle里向我们的注解处理器传递参数

Anroid-apt是用在Android Studio中处理注解处理的插件。他的作用如下

  1. 只允许配置编译时注解处理器依赖,但在最终APK或者Library中不包含注解处理器的代码。
apt project(':compiler')
  1. 这个插件可以自动的帮你为生成的代码创建目录,使注解处理器生成的代码能被Android Studio正确的引用,让生成的代码编译到APK里面去


    图片.png
  2. 从buildGradle里向我们的注解处理器传递参数
apt{
    arguments{
        module "app"
    }
}

处理器接收

Map<String, String> options = processingEnvironment.getOptions();
        Set<Map.Entry<String, String>> entries = options.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            ss = entry.getValue() + ss;
            messager.printMessage(Diagnostic.Kind.NOTE, entry.getKey() + "----" + entry.getValue());
        }

老版本的用法
整个工程的buildGradle你需要

 classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

而在你的app中需要这样

apply plugin: 'com.neenbedankt.android-apt'
dependencies{
    compile project(':annotation')
    apt project(':compiler')
    compile project(':api')
    }

到这里也许该松口气了,但是不好的消息来了android-apt插件作者近期已经发表声明表示后续不会再继续维护该插件。但也有个好消息是

Gradle从2.2版本开始支持annotationProcessor功能来代替Android-apt。另外,和android-apt只支持javac编译器相比,annotationProcessor同时支持javac和jack编译器

具体怎么用呢?
我们只需要在我们的app中这样加入

dependencies {
    compile project(':annotation')
    compile project(':api')
    annotationProcessor project(":compiler")
}

是不是很简单,如果你要传递参数你需要这样

defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ moduleName : project.getName() ]
            }
        }
    }

很方便调用但是有个前提==你的Gradle版本是2.2.X以上,就可以替换掉Android-apt。==

工程目录结构推荐

由于编译时注解处理器只在编译过程中使用,因此我们不希望注解处理器相关的代码在最终的APK中存在,这样能够有效的较少方法数。比如我通常在编写注解Annotation Processor的时候会引用javapoet和Guava,如果将这些代码也打进最终的APK中会造成方法数的暴增,因此建议将注解处理器相关代码单独成为一个模块。

另外为了方面注解被其他工程引用,通常我也建议将注解的定义单独划分成一个模块。

综上,我们最终的项目结构如下:

  • xxx/xxx-api:主工程/提供api,Android Library类型

  • xx-compiler:注解处理器模块,Java Library类型,打包apk时可以不要

  • xxx-annotations:自定义注解,Java Library类型,打包apk时可以不要
    xxx/xxx-api依赖xxx-annotations,xxx-compiler依赖xxx-annotations。

图片.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容