1. 程式人生 > >Android常用註解和APT的使用

Android常用註解和APT的使用

一、編譯時註解處理:

編譯時註解的核心依賴APT(Annotation Processing Tools)實現,原理是新增完註解後,在編譯時編譯器會檢查AbstractProcessor的子類,並且呼叫該型別的process函式,可以在該方法進行相應的處理。
1、定義自定義註解。新建一個Java library,名為 Annolib,定義一個自定義註解

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

2、定義註解處理器

。再次新建一個Java library,用來定義一個註解處理器,名為Processor:

@AutoService(javax.annotation.processing.Processor.class)
@SupportedAnnotationTypes({"example.mydemo.com.annolib.Person"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class Processor extends AbstractProcessor{

    @Override
    public boolean
process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { ..... } }
  • @AutoService(Processor.class),用於自動為 JAVA Processor 生成 META-INF 資訊。方便註冊註解處理器
  • @SupportedAnnotationTypes({}),宣告 Processor 處理的註解,注意這是一個數組,表示可以處理多個註解;也可以通過getSupportedAnnotationTypes 方法來處理
  • @SupportedSourceVersion(SourceVersion.RELEASE_7),宣告支援的原始碼版本。也可以通過getSupportedSourceVersion方法來處理。
  • process() 方法的實現,分為兩個步驟:
    (1)收集 Class 內的所有被自定義註解的類;
    (2)生成 .java 原始檔。可以使用JavaPoet開源庫進行編寫,提升效率。

Element的子類有:

  • ExecutableElement:
    表示某個類或介面的方法、構造方法或初始化程式(靜態或例項),包括註釋型別元素。
    對應@Target(ElementType.METHOD) @Target(ElementType.CONSTRUCTOR)

  • PackageElement;
    表示一個包程式元素。提供對有關包極其成員的資訊訪問。
    對應@Target(ElementType.PACKAGE)

  • TypeElement;
    表示一個類或介面程式元素。提供對有關型別極其成員的資訊訪問。
    對應@Target(ElementType.TYPE)
    注意:列舉型別是一種類,而註解型別是一種介面。

  • TypeParameterElement;
    表示一般類、介面、方法或構造方法元素的型別引數。
    對應@Target(ElementType.PARAMETER)

  • VariableElement;
    表示一個欄位、enum常量、方法或構造方法引數、區域性變數或異常引數。
    對應@Target(ElementType.LOCAL_VARIABLE)

Processor完整程式碼:

@AutoService(javax.annotation.processing.Processor.class)
@SupportedAnnotationTypes({"example.mydemo.com.annolib.BindView"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class Processor extends AbstractProcessor{

    /**
     * 指定支援的Java版本,
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 指定處理器需要處理哪些註解
     * @return 儲存註解的set
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> type = new LinkedHashSet<>();
        type.add(BindView.class.getCanonicalName());
        return type;
    }

    /**
     * 註解器核心程式碼,return false是不做任何事情
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        Messager messager = processingEnv.getMessager();
        //通過自定義的註解拿到element的集合
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
        //遍歷所有被註解的程式元素
        for (Element element : elements) {
            ElementKind kind = element.getKind();
            //判斷該元素是否為欄位
            if (kind == ElementKind.FIELD) {
                //通過message輸出
                messager.printMessage(Diagnostic.Kind.NOTE,"field:" + element.toString());
            }
        }
        return true;
    }
}

build.gradle:

apply plugin: 'java-library'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //依賴自定義註解
    implementation project(':Annolib')
    //用於生成 .java 原始檔
    implementation 'com.squareup:javapoet:1.11.1'
    //用於自動為 JAVA Processor 生成 META-INF 資訊。
    compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
}

sourceCompatibility = "1.7"
targetCompatibility = "1.7"

3、app module中使用註解:
(1)build.gradle:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
    //自定義註解庫的依賴
    implementation project(':Annolib')
    //自定義註解處理器的依賴
    annotationProcessor project(':processor')
}

annotationProcessor 僅僅在編譯期去依賴註解處理器所在庫進行工作,不會打包到APK 中。

(2)註解使用:

 @BindView(id = R.id.tv_title)
 TextView mTextView;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);

 }

通過命令 ./gradlew build去構建工程,輸出了註解View的Id:
這裡寫圖片描述

二、Android中常用註解

依賴:
在build.gradle中增加:

implementation 'com.android.support:support-annotations:27.1.1'   

官方文件:
https://developer.android.com/reference/android/support/annotation/package-summary
1、@NonNull 宣告方法的引數不能為空

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    String name = null;
    initView(name);
}
private void initView(@NonNull String s) {

}

2、@Nullable註解表示一個方法的引數或者返回值可以是Null

 private void initView(@Nullable String s) {

    }

3、@StringRes註解表示需要一個String型別的資源id

 private void initView(@StringRes int strId) {

    }

4、@IdRes 表示需要一個資源id

private void initView(@IdRes int id) {

    }

5、@IntDef 用來替代列舉:

public static final int ONE = 1;
public static final int TWO = 2;
public static final int THREE = 3;

@IntDef({ONE, TWO, THREE})
public @interface Common{

}

public void setValue(@Common int value) {

}


private void initView() {
    setValue(ONE);
}