Android開發利器之註解

Android註解筆記


簡介

關於註解的文章網上也很多了,文章也很不錯,本文總結記錄在開發註解時所踩得坑,希望對你在開發中有所幫助。

不太清楚註解是何物?以及如何開發使用註解可以參考一下鏈接:

秒懂,Java 註解 (Annotation)你可以這樣學
Android中註解的使用
Android編譯時註解


註解概念

註解,可以理解爲一種標籤!對代碼貼上(標籤)註解,能使我們快速認識代碼,以及輔助代碼完成某一特定的功能。


註解

@Retention(CLASS) @Target(FIELD)
public @interface BindView {
  /** View ID to which the field will be bound. */
  @IdRes int value();
}

作用對象@Target

它指明我們這個BindView註解可以用在哪些地方,@Target可以取以下值:

public enum ElementType {
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
}

比如Android中的ButterKnife中@BindView爲FILED,可以用在成員屬性上


生存範圍@Retention

它指明註解存活使用的生命範圍,可以取以下值:

public enum RetentionPolicy {
    SOURCE,  源代碼有效
    CLASS,	 編譯時有效
    RUNTIME		運行時有效
}

如@Override,爲SOURCE只是在源代碼有效,起標識作用,因爲方法重寫是java的語言特性,而不是Override帶來的功能,所以只是標識作用,重寫方法時要不要他都無所謂


註解成員

註解內部沒有方法,只有成員以及默認default初值,在我們使用註解的時候爲這些成員賦值,最後在註解解析器時會用到這些值


註解原理

開發時,我們開發了註解,並完成了作用對象和生存範圍,然後再把我們開發的註解,如BindView用到其他代碼處,這個時候除了SOURCE類型的註解外,還不能正常工作,因爲它還需要一個註解解析器

一般來說,註解解析器都是運用了反射原理,掃描或者獲取java類中的註解,然後反射獲取該註解修飾的成員、方法、參數等,然後把註解的賦值,通過反射爲這些成員賦值,或者調用其他方法來完成相應的功能


編譯時註解

編譯時註解只在編譯時有效,一般來說,編譯時註解是爲了生存一些中間輔助類,然後運行時用到。
首先,我們在開發在Module裏面開發,然後在主工程app內引入
在這裏插入圖片描述
apt-annotation是我們開發的註解工程(如BindView.java),java-lib
apt-api是開發註解的注入工程(如ButterKnife.java),android-lib
apt-processor則是註解解析器工程,java-lib,這部分通過annotationProcessor引入主工程,源代碼將不會封裝到apk

  1. apt-processor需要引入的工程:
implementation project(':apt-annotation')
implementation 'com.squareup:javapoet:1.10.0'
implementation 'com.google.auto.service:auto-service:1.0-rc4'

apt-annotation是定義的註解工程,因爲註解解析器需要知道我們要分析哪些註解
javapoet依賴主要是用於編譯時執行
auto-service是爲了讓我們自動執行我們的解析器用的
但是這裏我開發完成後並沒有執行,最後還是我執行了我的註解解析器在哪裏才執行了
在apt-processor工程下創建src/main/resources/META-INF/services/javax.annotation.processing.Processor,在這個文本文件中指明我的解析器,編譯就自動生成了我的java文件

  1. 開發解析器
@AutoService(Processor.class)
//我需要解析哪些註解,可以是多個參數
@SupportedAnnotationTypes("com.my.BindeView")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class XAptProcessor extends AbstractProcessor{
	
	@Override 這個方法就是解析器的核心,執行邏輯再此
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
		Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindeView.class);
        for (Element ele : elements) {
			1. 拿到每個Element的所屬類
			2. 獲取ele的註解以及註解裏面的值
			3. 類名爲key 註解以及ele爲value存儲到Map中
		}
		讀取完所有註解後
		4. 依次取出每個類,以及其註解
		5. 爲每個類創建方法MethodSpec
		6. 爲每個類創建輔助類TypeSpec
		7. 創建java文件JavaFile
	}
}
  1. 編寫註解注入類,在代碼中調用此類,利用反射生成我們的輔助類
public class XcInject {
    public static void bind(Object host) {
        String name = host.getClass().getName();
        try {
            Class<?> aClass = Class.forName(name + "_ViewInjector");
            Method method = aClass.getMethod("inject", host.getClass(), Object.class);
            method.invoke(aClass.newInstance(),host,host);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

最後,我們在代碼中調用XcInject.bind(this),就是在內存中生成我們的輔助類了,在用反射調用方法即可


運行時註解

運行時註解和上面類似,但是是在運行時JVM中執行,在運行時,通過獲取類的class,然後獲取註解以及修飾成員,反射進行賦值等


總結

註解是爲了提高我們開發效率而產生的,要注意它的使用場景和需求,因爲他很多地方都用到了反射原理,如果某些場景優先時間效率,儘量不要使用運行時註解,建議換成編譯時註解,使用反射較少,或者用其他代碼邏輯替換

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章