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閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容