打造一个简易的编译时注解框架(一)

一、AbstractProcessor

1,概述

注解分为编译时注解和运行时注解,现在流行的主流框架ButterKnife,Retrofit,Dragger都是编译时注解。编译时注解的核心依赖APT(Annotation Processing Tools)实现的,原理是在某些代码元素上(如类型、函数、字段等)添加注解,在编译时编译器会检查AbstractProcessor的子类,并且调用process函数,然后将添加了注解的所有元素都传递到process函数中,使得开发人员可以在编译器进行相应的处理。

二、创建步骤

1,首先使用Android Studio创建一个Android的project。然后开始创建一个名为processor的java library。 点击file->new->new module如图

Paste_Image.png

我们需要创建一个非Android的library,注意一定要选择Java Library
这里我们取名叫ButterKnifeProcessor

2,兼容性配置

在app的工程下的build.gradle,进行配置

Paste_Image.png

加入这句话

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}

如图:

Paste_Image.png

3,创建Annotation

创建annotation的Java moduel,里面放置注解类,如图:

Paste_Image.png

4,创建注解处理器

在process模块下创建一个处理器,这个继承AbstractProcessor,如图:

Paste_Image.png
Paste_Image.png

这个类上可以添加注解:
@SupportedAnnotationTypes的值为当前类支持的注解的完整类路径,支持通配符。如图也就是注解类的类路径
@SupportedSourceVersion 标识该处理器支持的源码版本
该类的源码:

@SupportedAnnotationTypes("com.example.BindView")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ButterKnifeProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        StringBuilder builder = new StringBuilder()
                .append("package com.example.generated;\n\n")
                .append("public class GeneratedClass {\n\n") // open class
                .append("\tpublic String getMessage() {\n") // open method
                .append("\t\treturn \"");


        // for each javax.lang.model.element.Element annotated with the CustomAnnotation
        for (Element element : roundEnv.getElementsAnnotatedWith(BindView.class)) {
            String objectType = element.getSimpleName().toString();
            // this is appending to the return statement
            builder.append(objectType).append(" says hello!\\n");
        }

        builder.append("\";\n") // end return
                .append("\t}\n") // close method
                .append("}\n"); // close class

        try { // write the file
            JavaFileObject source = processingEnv.getFiler().createSourceFile("com.example.generated.GeneratedClass");
            Writer writer = source.openWriter();
            writer.write(builder.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            // Note: calling e.printStackTrace() will print IO errors
            // that occur from the file already existing after its first run, this is normal
        }
        return true;
    }
}```

***这里的process方法生成了一个Java文件,在上面的方法中我们想在这个生成这个类和类中的方法,也就是上面StringBuilder 里面连接的字符串,用来测试,这里先不要管这个类是怎么生成的后面会说的***

package com.example.generated;

public class GeneratedClass {

public String getMessage() {
    return "mTextView says hello!\nmTextView1 says hello!\n";
}

}```

5,创建resource文件

创建好注解处理器后,我们需要告诉编译器在编译的时候使用哪个注解处理器,这里就需要创建javax.annotation.processing.Processor文件在processor模块下,main目录中创建一个resources文件夹,然后下边在创建META-INF/services,最后里边一个javax.annotation.processing.Processor文件,如下:

Paste_Image.png

这个txt文件下写下注解器的路径,如:

Paste_Image.png

选择上图中红色部分,就能将这个类的路径copy出来

6,添加android-apt

在project下的build.gradle中添加apt插件

Paste_Image.png

添加依赖:

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

再在app的build.gradle中添加

apply plugin: 'com.neenbedankt.android-apt'

Paste_Image.png

7,设置build的依赖

注解处理器编译生成一个jar,然后把这个jar包复制到app模块下。然后让app依赖引用这个jar。
首先配置app的build.gradle依赖项

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.0'
compile 'com.android.support.constraint:constraint-layout:1.0.0-beta5'
compile project(':annotation')
apt project(':processor')
testCompile 'junit:junit:4.12'
}

然后我们编写一个gradle task,把生成的jar文件复制到app/libs目录中。

task processorTask(type: Copy) {
from '../processor/build/libs/processor.jar' into 'libs/'
}
processorTask.dependsOn(':processor:build')
preBuild.dependsOn(processorTask)

这里的processor是Java moduel的名字,换成你自己的名字即可
完整的build:

Paste_Image.png

8,使用注解

Paste_Image.png

先执行Clean Project,然后再执行ReBuild Project,如果BUILD SUCCESSFUL
当然也可以在libs中看到生成的jar文件
如图:

Paste_Image.png

然后我们可以在下述位置查看到生成的Java文件

app/build/generated/source/apt/debug/package/GeneratedClass.java

如图:

Paste_Image.png

此时这个类的代码是这个样的

Paste_Image.png

是不是在此时,觉得很神奇,怎么在ButterKnifeProcessor 中process中StringBuilder连接的字符串怎么在这里生成了,
返回去看那个类的源码,是不是思路一下的就清晰啦,通过反射注解,编译器在 rebuild的时候就会检查ButterKnifeProcessor 这个类,调用process方法,所以效率是很高的

9,验证是否能使用

在app中的MainActivity中使用,代码如下:

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.textView)
    TextView mTextView;
    @BindView(R.id.textView1)
    TextView mTextView1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
    
     showMessage();

    }

    private void showMessage() {
        GeneratedClass generatedClass = new GeneratedClass();
        String message = generatedClass.getMessage();
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage(message)
                .setTitle("测试编译时")
                .setPositiveButton("ok", null).show();
    }
}

Paste_Image.png

下一篇:
http://www.jianshu.com/p/c516036506fd
参考文档
http://blog.stablekernel.com/the-10-step-guide-to-annotation-processing-in-android-studio

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,342评论 25 707
  • 本文章涉及代码已放到github上annotation-study 1.Annotation为何而来 What:A...
    zlcook阅读 29,114评论 15 116
  • 编译时注解处理 若希望对编译时的注解进行处理需要做 自定义类集成自AbstractProcessor 重写其中的p...
    生活理当如此阅读 8,815评论 3 18
  • 前面写了Android 开发:由模块化到组件化(一),很多小伙伴来问怎么没有Demo啊?之所以没有立刻放demo的...
    涅槃1992阅读 8,004评论 4 37
  • 视图控制器 1. 为什么要有视图控制器 我们开发中,可能会遇到某个界面比较复杂,要进行多个界面的切换,如果把这些界...
    Grt婷阅读 225评论 0 0