Android常用註解和APT的使用
一、編譯時註解處理:
編譯時註解的核心依賴APT(Annotation Processing Tools)實現,原理是新增完註解後,在編譯時編譯器會檢查AbstractProcessor的子類,並且呼叫該型別的process函式,可以在該方法進行相應的處理。
1、定義自定義註解。新建一個Java library,名為 Annolib,定義一個自定義註解
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int id();
}
2、定義註解處理器
@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"
- https://github.com/square/javapoet 方便生成.java檔案
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);
}