在 Android Studio 中使用 Annotation Processor

huazhiyuan 8年前發布 | 35K 次閱讀 Android開發 移動開發 Android Studio

Java 的 Annotation Processor 是非常有用的功能,很多常用的庫和框架都使用了 Annotation Processor 來生成代碼,比如 Butter Knife 就用來生成 findViewById 等代碼。

對于一些模板代碼使用 Annotation Processor 來自動生成可以提高編寫代碼的效率和質量,手工編寫畢竟容易出現紕漏,工具自動生成是有質量保證的。本文是由 Aitor Viana 編寫的如何在 Android Studio 中使用 Annotation Processor 的介紹。

Annotation Processor 主要涉及 3 部分,注解本身(Annotation)、注解處理器(Annotation Processor)以及 在 Android Studio 中如何使用注解處理器。

本文通過如何使用注解來自動生成 Parcelable 接口的代碼。

注解

注解就不用詳細介紹了,在 Android 編碼中應該經常使用。自定義一個表示需要自動生成 Parcelable 的注解: AutoParcel。

在 Android Studio 中創建一個 Java module,名字為 library 。在這個模塊中創建這個自定義的 AutoParcel 注解類:

package com.example.autoparcel;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE) // 代表在類級別上才能使用該注解
@Retention(RetentionPolicy.SOURCE) // 代表該注解只存在源代碼中,編譯后的字節碼中不存在
public @interface AutoParcel {}
 

由于 AutoParcel 只在源代碼中存在,編譯后沒有在字節碼中,所以最最終的運行時是沒有影響的。

由于這個 library 庫需要在 Android 項目中引用,所以需要修改其 gradle 文件制定編譯的 Java 語言版本(library/build.gradle ):

applyplugin: 'java'
 
// This module will be used in Android projects, need to be
// compatible with Java 1.7
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
 
dependencies {
    ...
}
 

注解處理器

注解處理器的功能就是用來讀取代碼中的注解然后來生成相關的代碼。

創建一個 Java module 名字為 “compiler”。 該模塊在編譯的時候,來獲取哪些類使用了 AutoParcel 注解,然后把繼承這些類實現 Parcelable 的代碼。該模塊并不在 Android 項目中引用,只存在于編譯的時候。所以這個模塊的 Java 版本號可以隨意指定(Java 8 、9)。

創建一個 AutoParcelProcessor 類來處理注解:

package com.example.autoparcel.codegen;
@SupportedAnnotationTypes("com.example.autoparcel.AutoParcel")
public final class AutoParcelProcessor extends AbstractProcessor {
  @Override
  public boolean process(
          Set<? extends TypeElement> annotations, 
          RoundEnvironmentenv) {
    ...
  }
}
 

對于該類有幾點要求:

1. 需要繼承至 AbstractProcessor

2. 需要使用類的全稱(包含包名)來指定其支持的注解類型(com.example.autoparcel.AutoParcel)

3. 實現 process() 函數,在該函數中來處理所支持的注解類型并生成需要的代碼。

下面只是介紹了實現 process() 函數的關鍵部分,完整代碼參考最后的項目。

如果沒有其他處理器需要繼續處理該注解,則 process() 返回 true。針對我們這個情況,只有 AutoParcelProcessor 需要處理 AutoParcel 注解,所以該函數返回 true。

package com.example.autoparcel.codegen;
...
@Override
public boolean process(
      Set<? extends TypeElement> annotations, 
      RoundEnvironmentenv) {
 
    Collection<? extends Element> annotatedElements =
            env.getElementsAnnotatedWith(AutoParcel.class);
    List<TypeElement> types = 
          new ImmutableList.Builder<TypeElement>()
            .addAll(ElementFilter.typesIn(annotatedElements))
            .build();
 
    for (TypeElementtype : types) {
        processType(type);
    }
 
    // 返回 true ,其他處理器不關心 AutoParcel  注解
    return true;
}
private void processType(TypeElementtype) {
    String className = generatedSubclassName(type);
    String source = generateClass(type, className);
    writeSourceFile(className, source, type);
}
private void writeSourceFile(
        String className, 
        String text, 
        TypeElementoriginatingType) {
    try {
        JavaFileObjectsourceFile =
            processingEnv.getFiler().
                createSourceFile(className, originatingType);
        Writerwriter = sourceFile.openWriter();
        try {
            writer.write(text);
        } finally {
            writer.close();
        }
    } catch (IOException e) {// silent}
}
...
 

注解處理器類編寫完后,還需要創建一個 java META_INF 文件來告訴系統具有注解處理功能。Java 代碼在編譯的時候,系統編譯器會查找所有的 META_INF 中的注冊的注解處理器來處理注解。

在 Android studio 的 compiler 項目中創建如下目錄:

compiler/src/main/resources/META_INF/services

在 services 目錄下面創建一個名字為 “javax.annotation.processing.Processor” 的文本文件:

該文件中每行一個注解處理器的全名:

com.example.autoparcel.codegen.AutoParcelProcessor
 

這樣,注解處理器就創建好了。

在 Android Studio 中使用

在 Android Studio 跟目錄的 settings.gradle 中添加前面創建的兩個模塊:

include ':app', ':compiler', ':library'
 

在 app/build.gradle 中添加前面創建的兩個模塊為依賴項:

...
dependencies {
    ...
    providedproject(':library')
    aptproject(':compiler')
    ...
}
 

注意上面 library 項目使用的是 provided 依賴,這是由于 provided 中的代碼只在編譯的時候存在,并不會打包到最終的應用中去,所以可以使用 provided。 二 compiler 項目為注解編譯器,通過使用 android-apt 插件來指定 apt 選項。

apt 是 Annotation Processing Tool 的縮寫。

現在就可以在項目中使用 AutoParcel 注解了:

@AutoParcel
public class Foo{
...
}
 

使用 Auto-Parcel 就再也不需要手工編寫 Parcelable 相關的代碼啦。解放雙手從今天開始!

 

來自:http://blog.chengyunfeng.com/?p=1021

 

 本文由用戶 huazhiyuan 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!