您好,登錄后才能下訂單哦!
小編給大家分享一下怎么使用Android注解處理器,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
推薦New -> Module -> Java Library,新建一個Java Library Module,命名為xx-annotation。用來單獨存放注解。
既然是注解處理器,那么首先需要有注解。自定義一個注解使用@interface
關鍵字。
public @interface LiveData { }
然后我們需要用到注解的注解,也就是元注解來控制注解的行為。這里我簡單介紹一些元注解。
Retention 表示注解的保留范圍。值用RetentionPolicy
枚舉類型表示,分為CLASS
、RUNTIME
、 SOURCE
。
Target
表示注解的使用范圍。值用ElementType
枚舉類型表示,有TYPE
(作用于類)、FIELD
(作用于屬性)、METHOD
(作用于方法)等。
這里我的@LiveData
注解作用是為了便于創建LiveData
,而創建時需要知道數據類型。所以這個注解的使用范圍就是類和屬性。
其次這個注解處理生成模板代碼后,我們不需要保留在編譯后的.class文件中。所以可以使用SOURCE
。
@Retention(RetentionPolicy.SOURCE) @Target({ElementType.FIELD, ElementType.TYPE}) public @interface LiveData { }
首先New -> Module -> Java Library,新建一個Java Library Module,命名為xx-complier。用來存放注解處理器。
創建一個繼承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
的類型。
ElementType | ElementKind | Element |
---|---|---|
TYPE | CLASS | TypeElement |
FIELD | FIELD | VariableElement |
METHOD | METHOD | ExecutableElement |
下面是封裝的簡易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; }
注冊處理器才可以使處理器生效,使用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 { }
項目的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(可選)
注意兩個端口號一致。然后選擇添加的“APT”,點擊debug按鈕啟動。
后面就是打斷點,編譯項目即可。
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 支持增量編譯。
添加依賴:
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注解處理器”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。