91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么使用Android注解處理器

發布時間:2021-04-13 14:33:07 來源:億速云 閱讀:269 作者:小新 欄目:開發技術

小編給大家分享一下怎么使用Android注解處理器,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

1.定義注解

推薦New -> Module -> Java Library,新建一個Java Library Module,命名為xx-annotation。用來單獨存放注解。

既然是注解處理器,那么首先需要有注解。自定義一個注解使用@interface關鍵字。

public @interface LiveData {
}

然后我們需要用到注解的注解,也就是元注解來控制注解的行為。這里我簡單介紹一些元注解。

  • Retention 表示注解的保留范圍。值用RetentionPolicy枚舉類型表示,分為CLASSRUNTIMESOURCE

  • Target 表示注解的使用范圍。值用ElementType枚舉類型表示,有TYPE(作用于類)、FIELD(作用于屬性)、METHOD(作用于方法)等。

這里我的@LiveData注解作用是為了便于創建LiveData,而創建時需要知道數據類型。所以這個注解的使用范圍就是類和屬性。

其次這個注解處理生成模板代碼后,我們不需要保留在編譯后的.class文件中。所以可以使用SOURCE

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface LiveData {
}

2.實現處理器

首先New -> Module -> Java Library,新建一個Java Library Module,命名為xx-complier。用來存放注解處理器。

怎么使用Android注解處理器

創建一個繼承AbstractProcessor的類LiveDataProcessor

public class LiveDataProcessor extends AbstractProcessor {

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

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Collections.singleton(LiveData.class.getCanonicalName());
    }

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

需要實現三個方法,getSupportedSourceVersion 指定支持的Java版本,getSupportedAnnotationTypes指定處理的注解。process是處理注解的地方。

不過這里還需要初始化一些工具,可以重寫init 來實現。

private Elements elementUtils; // 操作元素的工具類
private Filer filer;  // 用來創建文件
private Messager messager; // 用來輸出日志、錯誤或警告信息

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    this.elementUtils = processingEnv.getElementUtils();
    this.filer = processingEnv.getFiler();
    this.messager = processingEnv.getMessager();
}

下面就是重點process了,我們的注解作用范圍是類和屬性。所以我們需要將同一個類下的注解整理到一起。這里使用getElementsAnnotatedWith循環所有注解元素。

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

    for (Element element : roundEnv.getElementsAnnotatedWith(LiveData.class)) {
        if (element.getKind() == ElementKind.FIELD) {
            handlerField((VariableElement) element); // 表示一個字段
        }
        if (element.getKind() == ElementKind.CLASS) {
            handlerClass((TypeElement) element); // 表示一個類或接口
        }
        // ExecutableElement表示某個類或接口的方法
    }
    return true;
}

private void handlerClass(TypeElement element) {
    ClassEntity classEntity = new ClassEntity(element);
    String className = element.getSimpleName().toString();

    if (classEntityMap.get(className) == null) {
        classEntityMap.put(className, classEntity);
    }
}

private void handlerField(VariableElement element) {
    FieldEntity fieldEntity = new FieldEntity(element);
    String className = fieldEntity.getClassSimpleName();
    if (classEntityMap.get(className) == null) {
        classEntityMap.put(className,
                new ClassEntity((TypeElement) element.getEnclosingElement()));
    }
    ClassEntity classEntity = classEntityMap.get(className);
    classEntity.addFieldEntity(fieldEntity);
}

上面代碼中的element.getKind()獲取的是元素種類,對應的基本是上面元注解ElementType的類型。

ElementTypeElementKindElement
TYPECLASSTypeElement
FIELDFIELDVariableElement
METHODMETHODExecutableElement

下面是封裝的簡易element,便于實際的使用。

class ClassEntity {
    private final TypeElement element;
    private final Name classSimpleName;
    private final Map<String, FieldEntity> fields = new HashMap<>();

    public ClassEntity(TypeElement element) {
        this.element = element;
        this.classSimpleName = element.getSimpleName();
    }

    public String getClassSimpleName() {
        return classSimpleName.toString();
    }

    public void addFieldEntity(FieldEntity fieldEntity) {
        String fieldName = fieldEntity.getElement().toString();
        if (fields.get(fieldName) == null) {
            fields.put(fieldName, fieldEntity);
        }
    }

    public TypeElement getElement() {
        return element;
    }

    public Map<String, FieldEntity> getFields() {
        return fields;
    }
}

class FieldEntity {
    private VariableElement element;
    private String classSimpleName;

    public FieldEntity(VariableElement element) {
        this.element = element;
        this.classSimpleName = element.getEnclosingElement().getSimpleName().toString();
    }
    public VariableElement getElement() {
        return element;
    }

    public String getClassSimpleName() {
        return classSimpleName;
    }
}

下面就是使用JavaPoet來生成代碼,具體使用見JavaPoet使用攻略。這部分直接上代碼:

private JavaFile brewViewModel(Map.Entry<String, ClassEntity> item) {
    ClassEntity classEntity = item.getValue();
    LiveData liveData = classEntity.getElement().getAnnotation(LiveData.class);
    /*類名*/
    String className = classEntity.getElement().getSimpleName().toString() + "ViewModel";

    ClassName viewModelClazz = ClassName.get("androidx.lifecycle", "ViewModel");


    TypeSpec.Builder builder = TypeSpec
            .classBuilder(className)
            .addModifiers(Modifier.PUBLIC)
            .superclass(viewModelClazz);

    // 優先執行類LiveData注解
    if (liveData != null){
        TypeName valueTypeName = ClassName.get(classEntity.getElement());
        brewLiveData(classEntity.getClassSimpleName(), valueTypeName, builder);
    }else {
        Map<String, FieldEntity> fields = classEntity.getFields();

        for (FieldEntity fieldEntity : fields.values()){
            String fieldName = StringUtils.upperCase(fieldEntity.getElement().getSimpleName().toString());
            TypeName valueTypeName = ClassName.get(fieldEntity.getElement().asType());
            brewLiveData(fieldName, valueTypeName, builder);
        }
    }

    TypeSpec typeSpec = builder.build();
    // 指定包名
    return JavaFile.builder("com.zl.weilu.saber.viewmodel", typeSpec).build();
}

private void brewLiveData(String fieldName, TypeName valueTypeName, TypeSpec.Builder builder){

    String liveDataType;
    ClassName liveDataTypeClassName;

    liveDataType = "m$L = new MutableLiveData<>()";
    liveDataTypeClassName = ClassName.get("androidx.lifecycle", "MutableLiveData");

    ParameterizedTypeName typeName = ParameterizedTypeName.get(liveDataTypeClassName, valueTypeName);

    FieldSpec field = FieldSpec.builder(typeName, "m" + fieldName, Modifier.PRIVATE)
            .build();

    MethodSpec getMethod = MethodSpec
            .methodBuilder("get" + fieldName)
            .addModifiers(Modifier.PUBLIC)
            .returns(field.type)
            .beginControlFlow("if (m$L == null)", fieldName)
            .addStatement(liveDataType, fieldName)
            .endControlFlow()
            .addStatement("return m$L", fieldName)
            .build();

    MethodSpec getValue = MethodSpec
            .methodBuilder("get" + fieldName + "Value")
            .addModifiers(Modifier.PUBLIC)
            .returns(valueTypeName)
            .addStatement("return this.$N().getValue()", getMethod)
            .build();

    MethodSpec setMethod = MethodSpec
            .methodBuilder("set" + fieldName)
            .addModifiers(Modifier.PUBLIC)
            .returns(void.class)
            .addParameter(valueTypeName, "mValue")
            .beginControlFlow("if (this.m$L == null)", fieldName)
            .addStatement("return")
            .endControlFlow()
            .addStatement("this.m$L.setValue(mValue)", fieldName)
            .build();

    MethodSpec postMethod = MethodSpec
            .methodBuilder("post" + fieldName)
            .addModifiers(Modifier.PUBLIC)
            .returns(void.class)
            .addParameter(valueTypeName, "mValue")
            .beginControlFlow("if (this.m$L == null)", fieldName)
            .addStatement("return")
            .endControlFlow()
            .addStatement("this.m$L.postValue(mValue)", fieldName)
            .build();

    builder.addField(field)
            .addMethod(getMethod)
            .addMethod(getValue)
            .addMethod(setMethod)
            .addMethod(postMethod);

}

輸出文件:

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   ...
    for (Map.Entry<String, ClassEntity> item : classEntityMap.entrySet()) {
        try {
            brewViewModel(item).writeTo(filer);
        } catch (Exception e) {
            messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage(), item.getValue().getElement());
        }
    }
    return true;
}

3.注冊處理器

注冊處理器才可以使處理器生效,使用Google開源的AutoService的庫。

dependencies {
    implementation 'com.squareup:javapoet:1.13.0'
    implementation 'com.google.auto.service:auto-service:1.0-rc7'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
}

然后添加@AutoService注解即可。

@AutoService(Processor.class)
public class LiveDataProcessor extends AbstractProcessor {
   
}

4.調試注解處理器

項目的gradle.properties中配置:

org.gradle.daemon=true
org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

接著Run -> Edit Configurations -> 點擊左上角加號 -> 選擇 Remote -> 指定module(可選)

怎么使用Android注解處理器

注意兩個端口號一致。然后選擇添加的“APT”,點擊debug按鈕啟動。

后面就是打斷點,編譯項目即可。

5.支持增量編譯

Gradle 在 5.0 增加了對 Java 增量編譯的支持,通過增量編譯,我們能夠獲得一些優點:

  • 更少的編譯耗時

  • 更少的字節碼修改

如果注解處理器沒有支持增量編譯,那么編譯時,會輸出以下日志:

w: [kapt] Incremental annotation processing requested, but support is disabled because the following processors are not incremental: com.x.XXProcessor (NON_INCREMENTAL).

Gradle 支持兩種注解處理器的增量編譯:isolating 和 aggregating。

支持方法是在 META-INF/gradle/incremental.annotation.processors 文件中聲明支持增量編譯的注解處理器。

xx-complier/src/main/
├── java
│   ...
│   └── LiveDataProcessor
└── resources
    └── META-INF
        ├── gradle
        │   └── incremental.annotation.processors
        └── services
            └── javax.annotation.processing.Processor

incremental.annotation.processors內容如下:

com.zl.weilu.saber.compiler.LiveDataProcessor,aggregating

這部分詳細內容見 讓 Annotation Processor 支持增量編譯。

6.使用處理器

添加依賴:

dependencies {
    implementation project(':saber-annotation')
    annotationProcessor project(':saber-compiler')
}

首先創建一個類,使用@LiveData注解標記你要保存的數據。

public class SeekBar {

    @LiveData
    Integer value;
}

Build – > Make Project 生成代碼如下:

public class SeekBarViewModel extends ViewModel {
  private MutableLiveData<Integer> mValue;

  public MutableLiveData<Integer> getValue() {
    if (mValue == null) {
      mValue = new MutableLiveData<>();
    }
    return mValue;
  }

  public Integer getValueValue() {
    return getValue().getValue();
  }

  public void setValue(Integer mValue) {
    if (this.mValue == null) {
      return;
    }
    this.mValue.setValue(mValue);
  }

  public void postValue(Integer mValue) {
    if (this.mValue == null) {
      return;
    }
    this.mValue.postValue(mValue);
  }
}

至此,我們就完成了一個簡單的自定義處理器。

以上是“怎么使用Android注解處理器”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

岚皋县| 丽江市| 长寿区| 南京市| 临武县| 丰城市| 准格尔旗| 军事| 浮梁县| 延安市| 余干县| 临夏市| 南陵县| 井研县| 柞水县| 吉林省| 石首市| 桂东县| 潍坊市| 临沭县| 根河市| 宝应县| 龙岩市| 荆州市| 黑河市| 盐城市| 鹿泉市| 台北市| 疏勒县| 海门市| 荆门市| 故城县| 上思县| 万安县| 巩留县| 舞钢市| 油尖旺区| 桂平市| 乐至县| 双流县| 方山县|