打造一個(gè)簡(jiǎn)易的編譯時(shí)注解框架(一)

一囱修、AbstractProcessor

1捞烟,概述

注解分為編譯時(shí)注解和運(yùn)行時(shí)注解薄声,現(xiàn)在流行的主流框架ButterKnife当船,Retrofit,Dragger都是編譯時(shí)注解默辨。編譯時(shí)注解的核心依賴APT(Annotation Processing Tools)實(shí)現(xiàn)的德频,原理是在某些代碼元素上(如類型、函數(shù)缩幸、字段等)添加注解壹置,在編譯時(shí)編譯器會(huì)檢查AbstractProcessor的子類,并且調(diào)用process函數(shù)表谊,然后將添加了注解的所有元素都傳遞到process函數(shù)中钞护,使得開(kāi)發(fā)人員可以在編譯器進(jìn)行相應(yīng)的處理。

二铃肯、創(chuàng)建步驟

1患亿,首先使用Android Studio創(chuàng)建一個(gè)Android的project。然后開(kāi)始創(chuàng)建一個(gè)名為processor的java library押逼。 點(diǎn)擊file->new->new module如圖

Paste_Image.png

我們需要?jiǎng)?chuàng)建一個(gè)非Android的library步藕,注意一定要選擇Java Library
這里我們?nèi)∶蠦utterKnifeProcessor

2,兼容性配置

在app的工程下的build.gradle,進(jìn)行配置

Paste_Image.png

加入這句話

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}

如圖:

Paste_Image.png

3挑格,創(chuàng)建Annotation

創(chuàng)建annotation的Java moduel咙冗,里面放置注解類,如圖:

Paste_Image.png

4漂彤,創(chuàng)建注解處理器

在process模塊下創(chuàng)建一個(gè)處理器雾消,這個(gè)繼承AbstractProcessor,如圖:

Paste_Image.png
Paste_Image.png

這個(gè)類上可以添加注解:
@SupportedAnnotationTypes的值為當(dāng)前類支持的注解的完整類路徑挫望,支持通配符立润。如圖也就是注解類的類路徑
@SupportedSourceVersion 標(biāo)識(shí)該處理器支持的源碼版本
該類的源碼:

@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方法生成了一個(gè)Java文件,在上面的方法中我們想在這個(gè)生成這個(gè)類和類中的方法媳板,也就是上面StringBuilder 里面連接的字符串桑腮,用來(lái)測(cè)試,這里先不要管這個(gè)類是怎么生成的后面會(huì)說(shuō)的***

package com.example.generated;

public class GeneratedClass {

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

}```

5,創(chuàng)建resource文件

創(chuàng)建好注解處理器后蛉幸,我們需要告訴編譯器在編譯的時(shí)候使用哪個(gè)注解處理器破讨,這里就需要?jiǎng)?chuàng)建javax.annotation.processing.Processor文件在processor模塊下,main目錄中創(chuàng)建一個(gè)resources文件夾奕纫,然后下邊在創(chuàng)建META-INF/services提陶,最后里邊一個(gè)javax.annotation.processing.Processor文件,如下:

Paste_Image.png

這個(gè)txt文件下寫(xiě)下注解器的路徑匹层,如:

Paste_Image.png

選擇上圖中紅色部分隙笆,就能將這個(gè)類的路徑copy出來(lái)

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,設(shè)置build的依賴

注解處理器編譯生成一個(gè)jar撑柔,然后把這個(gè)jar包復(fù)制到app模塊下煤率。然后讓app依賴引用這個(gè)jar。
首先配置app的build.gradle依賴項(xiàng)

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'
}

然后我們編寫(xiě)一個(gè)gradle task乏冀,把生成的jar文件復(fù)制到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

先執(zhí)行Clean Project昼捍,然后再執(zhí)行ReBuild Project,如果BUILD SUCCESSFUL
當(dāng)然也可以在libs中看到生成的jar文件
如圖:

Paste_Image.png

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

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

如圖:

Paste_Image.png

此時(shí)這個(gè)類的代碼是這個(gè)樣的

Paste_Image.png

是不是在此時(shí),覺(jué)得很神奇肢扯,怎么在ButterKnifeProcessor 中process中StringBuilder連接的字符串怎么在這里生成了妒茬,
返回去看那個(gè)類的源碼,是不是思路一下的就清晰啦蔚晨,通過(guò)反射注解乍钻,編譯器在 rebuild的時(shí)候就會(huì)檢查ButterKnifeProcessor 這個(gè)類,調(diào)用process方法铭腕,所以效率是很高的

9银择,驗(yàn)證是否能使用

在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("測(cè)試編譯時(shí)")
                .setPositiveButton("ok", null).show();
    }
}

Paste_Image.png

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末累舷,一起剝皮案震驚了整個(gè)濱河市浩考,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌被盈,老刑警劉巖析孽,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異只怎,居然都是意外死亡袜瞬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)身堡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)邓尤,“玉大人,你說(shuō)我怎么就攤上這事盾沫〔迷” “怎么了殿漠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵赴精,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我绞幌,道長(zhǎng)蕾哟,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮谭确,結(jié)果婚禮上帘营,老公的妹妹穿的比我還像新娘。我一直安慰自己逐哈,他們只是感情好芬迄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著昂秃,像睡著了一般禀梳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肠骆,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天算途,我揣著相機(jī)與錄音,去河邊找鬼蚀腿。 笑死嘴瓤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的莉钙。 我是一名探鬼主播廓脆,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼磁玉!你這毒婦竟也來(lái)了狞贱?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蜀涨,失蹤者是張志新(化名)和其女友劉穎瞎嬉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體厚柳,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氧枣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了别垮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片便监。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖碳想,靈堂內(nèi)的尸體忽然破棺而出烧董,到底是詐尸還是另有隱情,我是刑警寧澤胧奔,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布逊移,位于F島的核電站,受9級(jí)特大地震影響龙填,放射性物質(zhì)發(fā)生泄漏胳泉。R本人自食惡果不足惜拐叉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扇商。 院中可真熱鬧凤瘦,春花似錦、人聲如沸案铺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)控汉。三九已至坝茎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間暇番,已是汗流浹背嗤放。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留壁酬,地道東北人次酌。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像舆乔,于是被迫代替她去往敵國(guó)和親岳服。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評(píng)論 25 707
  • 本文章涉及代碼已放到github上annotation-study 1.Annotation為何而來(lái) What:A...
    zlcook閱讀 29,158評(píng)論 15 116
  • 編譯時(shí)注解處理 若希望對(duì)編譯時(shí)的注解進(jìn)行處理需要做 自定義類集成自AbstractProcessor 重寫(xiě)其中的p...
    生活理當(dāng)如此閱讀 8,863評(píng)論 3 18
  • 前面寫(xiě)了Android 開(kāi)發(fā):由模塊化到組件化(一),很多小伙伴來(lái)問(wèn)怎么沒(méi)有Demo啊?之所以沒(méi)有立刻放demo的...
    涅槃1992閱讀 8,028評(píng)論 4 37
  • 視圖控制器 1. 為什么要有視圖控制器 我們開(kāi)發(fā)中希俩,可能會(huì)遇到某個(gè)界面比較復(fù)雜吊宋,要進(jìn)行多個(gè)界面的切換,如果把這些界...
    Grt婷閱讀 229評(píng)論 0 0