打造一個簡易的編譯時注解框架(一)

一、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.lxweimin.com/p/c516036506fd
參考文檔
http://blog.stablekernel.com/the-10-step-guide-to-annotation-processing-in-android-studio

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容

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