APT

APT-概念了解

友情鏈接:

https://lizhaoxuan.github.io/2016/07/17/apt-Grammar-explanation/

https://github.com/Gavin-ZYX/APTTest.git

apt

APT(Annotation Processing Tool)是一種處理注釋的工具残吩,它對源代碼文件進行檢測找出其中的Annotation袖迎,根據(jù)注釋自動生成代碼顿仇。Annotation處理器在出來Annotation時可以根據(jù)源文件中的Annotation生成額外的源文件和其它的文件(文件具體內(nèi)容由Annotation處理器的編寫者決定)东抹,APT還會編譯生成的源文件和原來的源文件,將它們一起生成class文件。

annotationProcessor

annotationProcessor是APT工具中的一種,他是google開發(fā)的內(nèi)置框架,不需要引入胶坠,可以直接在build.gradle文件中使用

android-apt

android-apt是由一位開發(fā)者自己開發(fā)的apt框架,源代碼托管在這里繁堡,隨著Android Gradle 插件 2.2 版本的發(fā)布沈善,Android Gradle 插件提供了名為 annotationProcessor 的功能來完全代替 android-apt ,自此android-apt 作者在官網(wǎng)發(fā)表聲明最新的Android Gradle插件現(xiàn)在已經(jīng)支持annotationProcessor椭蹄,并警告和或阻止android-apt 闻牡,并推薦大家使用 Android 官方插件annotationProcessor。

Demo-知識點

注解

@Retention

  • @Retention(RetentionPolicy.SOURCE) 源碼時注解绳矩,一般用來作為編譯器標記罩润。就比如Override, Deprecated, SuppressWarnings這樣的注解
  • @Retention(RetentionPolicy.RUNTIME) 運行時注解,一般在運行時通過反射去識別的注解
  • @Retention(RetentionPolicy.CLASS) 編譯時注解翼馆,在編譯時處理

@Target

  • @Target(ElementType.TYPE) 接口割以、類、枚舉应媚、注解
  • @Target(ElementType.FIELD)字段严沥、枚舉的常量
  • @Target(ElementType.METHOD) 方法
  • @Target(ElementType.PARAMETER) 方法參數(shù)
  • @Target(ElementType.CONSTRUCTOR) 構(gòu)造函數(shù)
  • @Target(ElementType.LOCAL_VARIABLE) 局部變量
  • @Target(ElementType.ANNOTATION_TYPE) 注解
  • @Target(ElementType.package) 包

@Inherited

該注解的字面意識是繼承,但你要知道注解是不可以繼承的中姜。
ie:當你的注解定義到類A上消玄,此時跟伏,有個B類繼承A,且沒使用該注解翩瓜。但是掃描的時候受扳,會把A類設置的注解,掃描到B類上

輸出Log

Messager

//取得Messager對象
Messager messager = processingEnv.getMessager();

Processor日志輸出的位置在編譯器下方的Messages窗口中

Processor支持最基礎(chǔ)的System.out方法

同樣Processor也有自己的Log輸出工具: Messager

同Log類似兔跌,Messager也有日志級別的選擇

  • Diagnostic.Kind.ERROR
  • Diagnostic.Kind.WARNING
  • Diagnostic.Kind.MANDATORY_WARNING
  • Diagnostic.Kind.NOTE
  • Diagnostic.Kind.OTHER

Element

Represents a program element such as a package, class, or method.
Each element represents a static, language-level construct (and not, for example, a runtime construct of the virtual machine).
表示一個程序元素勘高,比如包、類或者方法

ExecutableElement

表示某個類或接口的方法坟桅、構(gòu)造方法初始化程序(靜態(tài)或?qū)嵗┗ㄗ⑨岊愋驮亍?/p>

對應@Target(ElementType.METHOD) @Target(ElementType.CONSTRUCTOR)

PackageElement;

表示一個包程序元素。提供對有關(guān)包極其成員的信息訪問桦卒。

對應@Target(ElementType.PACKAGE)

TypeElement;

表示一個接口程序元素。提供對有關(guān)類型極其成員的信息訪問匿又。

對應@Target(ElementType.TYPE)

注意:枚舉類型是一種類方灾,而注解類型是一種接口。

TypeParameterElement;

表示一般類碌更、接口裕偿、方法或構(gòu)造方法元素的類型參數(shù)

對應@Target(ElementType.PARAMETER)

VariableElement;

表示一個字段痛单、enum常量嘿棘、方法或構(gòu)造方法參數(shù)局部變量異常參數(shù)旭绒。

對應@Target(ElementType.LOCAL_VARIABLE)

修飾方法的注解和ExecutableElement

當你有一個注解是以@Target(ElementType.METHOD)定義時鸟妙,表示該注解只能修飾方法。

那么這個時候你為了生成代碼挥吵,而需要獲取一些基本信息:包名重父、類名、方法名忽匈、參數(shù)類型房午、返回值。

//OnceClick.class 以 @Target(ElementType.METHOD)修飾
for (Element element : roundEnv.getElementsAnnotatedWith(OnceClick.class)) {
    //對于Element直接強轉(zhuǎn)
    ExecutableElement executableElement = (ExecutableElement) element;

    //非對應的Element丹允,通過getEnclosingElement轉(zhuǎn)換獲取
    TypeElement classElement = (TypeElement) element
                .getEnclosingElement();

    //當(ExecutableElement) element成立時郭厌,使用(PackageElement) element
    //            .getEnclosingElement();將報錯。
    //需要使用elementUtils來獲取
    Elements elementUtils = processingEnv.getElementUtils();
    PackageElement packageElement = elementUtils.getPackageOf(classElement);

    //全類名
    String fullClassName = classElement.getQualifiedName().toString();
    //類名
    String className = classElement.getSimpleName().toString();
    //包名
    String packageName = packageElement.getQualifiedName().toString();
    //方法名
    String methodName = executableElement.getSimpleName().toString();

    //取得方法參數(shù)列表
    List<? extends VariableElement> methodParameters = executableElement.getParameters();
    //參數(shù)類型列表
    List<String> types = new ArrayList<>();
    for (VariableElement variableElement : methodParameters) {
        TypeMirror methodParameterType = variableElement.asType();
        if (methodParameterType instanceof TypeVariable) {
            TypeVariable typeVariable = (TypeVariable) methodParameterType;
            methodParameterType = typeVariable.getUpperBound();

        }
        //參數(shù)名
        String parameterName = variableElement.getSimpleName().toString();
        //參數(shù)類型
        String parameteKind = methodParameterType.toString();
        types.add(methodParameterType.toString());
    }
}

修飾屬性雕蔽、類成員的注解和VariableElement

當你有一個注解是以@Target(ElementType.FIELD)定義時折柠,表示該注解只能修飾屬性、類成員批狐。

那么這個時候你為了生成代碼液走,而需要獲取一些基本信息:包名、類名、類成員類型缘眶、類成員名

如何獲戎龈:

for (Element element : roundEnv.getElementsAnnotatedWith(IdProperty.class)) {
    //ElementType.FIELD注解可以直接強轉(zhuǎn)VariableElement
    VariableElement variableElement = (VariableElement) element;

    TypeElement classElement = (TypeElement) element
            .getEnclosingElement();
    PackageElement packageElement = elementUtils.getPackageOf(classElement);
    //類名
    String className = classElement.getSimpleName().toString();
    //包名
    String packageName = packageElement.getQualifiedName().toString();
    //類成員名
    String variableName = variableElement.getSimpleName().toString();

    //類成員類型
    TypeMirror typeMirror = variableElement.asType();
    String type = typeMirror.toString();

}

修飾類的注解和TypeElement

當你有一個注解是以@Target(ElementType.TYPE)定義時,表示該注解只能修飾類巷懈、接口该抒、枚舉。

那么這個時候你為了生成代碼顶燕,而需要獲取一些基本信息:包名凑保、類名、全類名涌攻、父類欧引。

如何獲取:

for (Element element : roundEnv.getElementsAnnotatedWith(xxx.class)) {
    //ElementType.TYPE注解可以直接強轉(zhuǎn)TypeElement
    TypeElement classElement = (TypeElement) element;

    PackageElement packageElement = (PackageElement) element
                .getEnclosingElement();

    //全類名
    String fullClassName = classElement.getQualifiedName().toString();
    //類名
    String className = classElement.getSimpleName().toString();
    //包名
    String packageName = packageElement.getQualifiedName().toString();
     //父類名
     String superClassName = classElement.getSuperclass().toString();

}

Demo

module

apt-annotation (java-library)
apt-processor  (java-library)
apt-library    (com.android.library)

apt-annotation

注解類BindView(編譯時注解)

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
    int value();
}

build.gradle

apply plugin: 'java-library'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}

sourceCompatibility = "1.7"
targetCompatibility = "1.7"

apt-processor

build.gradle

apply plugin: 'java-library'

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.google.auto.service:auto-service:1.0-rc2'
    implementation 'com.squareup:javapoet:1.10.0'
    implementation project(':apt-annotation')
}

sourceCompatibility = "1.7"
targetCompatibility = "1.7"

AbstractProcessor

@SupportedAnnotationTypes({"com.example.gavin.apt_annotation.BindView"})
@AutoService(Processor.class)
public class TestProcessor extends AbstractProcessor {
  @Override
  public synchronized void init(ProcessingEnvironment processingEnvironment) {
    super.init(processingEnvironment);
  }

  @Override
  public Set<String> getSupportedAnnotationTypes() {
    return super.getSupportedAnnotationTypes();
  }

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

  @Override
  public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
    return false;
  }
}

添加自己需要處理的注解恳谎,可以通過兩種方式:getSupportedAnnotationTypes()或者直接用注解
@SupportedAnnotationTypes("全路徑")

process方法

mMessager.printMessage(Diagnostic.Kind.NOTE, "processing...");
    // 1.會執(zhí)行多次芝此,所以要先clear
    mProxyMap.clear();
    // 2.得到所有的注解并收集到map中
    Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
    for (Element element : elements) {
      VariableElement variableElement = (VariableElement) element;
      TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
      String fullClassName = classElement.getQualifiedName().toString();

      //elements的信息保存到mProxyMap中
      ClassCreatorProxy proxy = mProxyMap.get(fullClassName);
      if (proxy == null) {
        proxy = new ClassCreatorProxy(mElementUtils, classElement);
        mProxyMap.put(fullClassName, proxy);
      }
      BindView bindAnnotation = variableElement.getAnnotation(BindView.class);
      int id = bindAnnotation.value();
      proxy.putElement(id, variableElement);
    }
    // 3.通過遍歷mProxyMap,創(chuàng)建java文件 通過javapoet生成
    for (String key : mProxyMap.keySet()) {
      ClassCreatorProxy proxyInfo = mProxyMap.get(key);
      JavaFile javaFile = JavaFile.builder(proxyInfo.getPackageName(), proxyInfo.generateJavaCode2()).build();
      try {
        // 生成文件
        javaFile.writeTo(mFiler);
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    mMessager.printMessage(Diagnostic.Kind.NOTE, "process finish ...");
    return true;

注:APT有一個局限性因痛,就是只會掃描Java源碼婚苹,不會掃描jar ,aar 和class 鸵膏。也就是說膊升,所有模塊需要以源碼形式存在。而現(xiàn)在通用的做法是谭企,將模塊打包成jar或者aar廓译,發(fā)布到Maven庫,再由其他模塊自行引用债查,解決這個問題的基本方法用gradle plugin方法(項目中的medusa庫就是插件的方式)

第三方庫分析

Arouter

ButterKnife

EventBus

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末责循,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子攀操,更是在濱河造成了極大的恐慌院仿,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件速和,死亡現(xiàn)場離奇詭異歹垫,居然都是意外死亡,警方通過查閱死者的電腦和手機颠放,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門排惨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碰凶,你說我怎么就攤上這事暮芭÷雇眨” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵辕宏,是天一觀的道長畜晰。 經(jīng)常有香客問我,道長瑞筐,這世上最難降的妖魔是什么凄鼻? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮聚假,結(jié)果婚禮上块蚌,老公的妹妹穿的比我還像新娘。我一直安慰自己膘格,他們只是感情好峭范,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瘪贱,像睡著了一般纱控。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上政敢,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天其徙,我揣著相機與錄音胚迫,去河邊找鬼喷户。 笑死,一個胖子當著我的面吹牛访锻,可吹牛的內(nèi)容都是我干的褪尝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼期犬,長吁一口氣:“原來是場噩夢啊……” “哼河哑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起龟虎,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤璃谨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鲤妥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佳吞,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年棉安,在試婚紗的時候發(fā)現(xiàn)自己被綠了底扳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡贡耽,死狀恐怖衷模,靈堂內(nèi)的尸體忽然破棺而出鹊汛,到底是詐尸還是另有隱情,我是刑警寧澤阱冶,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布刁憋,位于F島的核電站,受9級特大地震影響熙揍,放射性物質(zhì)發(fā)生泄漏职祷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一届囚、第九天 我趴在偏房一處隱蔽的房頂上張望有梆。 院中可真熱鬧,春花似錦意系、人聲如沸泥耀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽痰催。三九已至,卻和暖如春迎瞧,著一層夾襖步出監(jiān)牢的瞬間夸溶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工凶硅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缝裁,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓足绅,卻偏偏與公主長得像捷绑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子氢妈,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

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