對於Android註解,或多或少都有一點接觸,但相信大多數人都是在使用其它依賴庫的時候接觸的。由於有些庫若是你想使用它就必須使用它所提供的註解。例如:ButterKnife、Dagger二、Room等等。java
至於爲什麼使用註解?使用過的應該都知道,最明顯的就是方便、簡潔。經過使用註解能夠在項目編譯階段,幫助咱們自動生成一些重複的代碼,減輕咱們的負擔。典型的ButterKnife本質就是使用Android註解,經過註解來減小咱們對view.findViewById的編寫,提升咱們的開發效率。上一個系列(AAC)的Room也是同樣,咱們能夠簡單的回顧一下:android
@Entity(tableName = "contacts") data class ContactsModel( @PrimaryKey @ColumnInfo(name = "contacts_id") val id: Int, @ColumnInfo(name = "name") val name: String, @ColumnInfo(name = "phone") val phone: String )
經過使用註解來定義一個實體表,也就10行左右的代碼。若是要咱們所有本身寫那絕對要兩三百行代碼了,並且其中還可能出錯,又要改bug等等。效率就嚴重下降。對於依賴庫若是都這麼麻煩也就不會有人用了。git
那麼如何判斷一個依賴庫是否須要使用註解呢?其實很簡單,只要記住如下兩點便可:github
這裏透露一下,Android註解的本質是使用Java的反射機制,後續會詳細說明
相信ButterKnife應該有接觸過吧,沒有的也不要緊,如今正是時候。下面咱們會本身實現BindView與OnClick註解,實現ButterKnife中的對應註解功能。那麼我先來看下總體的項目架構segmentfault
經過項目圖,咱們能夠清晰的看到,主要分爲三個部分api
爲了幫助你們可以更輕鬆的理解Android註解,今天主要分析的就是butterknife-annotations這個註解庫。帶你們一塊兒來聲明註解變量。數組
爲了要實現開源庫butterknife相似的綁定id效果,這裏咱們先定義一個BindView註解,具體以下:架構
@Retention(RetentionPolicy.SOURCE) @Target(ElementType.FIELD) public @interface BindView { @IdRes int[] value(); }
嗯,仍是很簡單的對吧。也就5行代碼解決BindView註解的定義。app
那麼再來詳細剖析這5行代碼。函數
首先是第一行代碼的Retention,看它的使用方式就能知道,它也是一個聲明瞭的註解。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ RetentionPolicy value(); }
經過源碼咱們能夠看出該註解只接收一個參數,該參數爲RetentionPolicy類型。那麼咱們在進一步深刻RetentionPolicy:
public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. */ SOURCE, /** * Annotations are to be recorded in the class file by the compiler * but need not be retained by the VM at run time. This is the default * behavior. */ CLASS, /** * Annotations are to be recorded in the class file by the compiler and * retained by the VM at run time, so they may be read reflectively. * * @see java.lang.reflect.AnnotatedElement */ RUNTIME }
在這裏咱們發現它實際上是一個枚舉,在枚舉中支持三個常量,分別爲SOURCE、CLASS與RUNTIME。它們的區別主要是做用的週期範圍,下面我再對這三個的做用進行翻譯一遍:
因此它們的存在的生命時長爲SOURCE < CLASS < RUNTIME。知道了它的做用範圍以後,咱們在自定義註解時就要儘可能較小注解的做用範圍,提升項目的編譯與運行速度。
由於咱們的BindView註解只是爲了進行Viwe的綁定,因此在編譯以後就無需存在,因此這裏就使用了CLASS來進行標明。
下面咱們在來看第二行代碼,這裏使用到了另外一個註解Target,咱們仍是來看下它的源碼:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /** * Returns an array of the kinds of elements an annotation type * can be applied to. * @return an array of the kinds of elements an annotation type * can be applied to */ ElementType[] value(); }
能夠看到註解的源碼都很是簡單,這裏接收了一個ElementType數組參數,ElementType不難猜出它的類型也是一個枚舉:
public enum ElementType { /** Class, interface (including annotation type), or enum declaration */ TYPE, /** Field declaration (includes enum constants) */ FIELD, /** Method declaration */ METHOD, /** Formal parameter declaration */ PARAMETER, /** Constructor declaration */ CONSTRUCTOR, /** Local variable declaration */ LOCAL_VARIABLE, /** Annotation type declaration */ ANNOTATION_TYPE, /** Package declaration */ PACKAGE, /** * Type parameter declaration * * @since 1.8 */ TYPE_PARAMETER, /** * Use of a type * * @since 1.8 */ TYPE_USE }
ElementType中雖然有10常量,但咱們實際真正經常使用的也就是前面8種。它們表明自定義的註解可以做用的對象。分別爲:
結合咱們的BindView的做用是對View進行id綁定,天然是做用與聲明的字段上。因此在BindView中使用了FIELD。
再來看第四行代碼
@IdRes int[] value()
有了上面的註解接觸,不難理解這是標明BindView將接收一個int類型的數組參數。對於開源庫butterknife中的BindView是接收須要綁定的View的id,這裏咱們作一個改版,再接收一個String的id,用來爲綁定的View設置默認值。這樣咱們自定義了的BindView註釋就完成了。
下面咱們再自定義一個OnClick點擊的註解,通過上面的分析,能夠在腦海中想一想Retention與Target分別什麼值?
想好了以後咱們在來過一遍
@Retention(RetentionPolicy.SOURCE) @Target(ElementType.METHOD) public @interface OnClick { @IdRes int value(); }
Retention的做用範圍與BindView同樣首頁SOURCE,在編譯以後就無需存在;Target的做用對象與BindView不一樣,既然是點擊事件的點擊操做,天然是做用在操做邏輯的方法上,因此這裏使用METHOD。
文章開頭有說起到本質是經過註解來自動生成代碼,爲咱們建立所需的類,那麼在實際開發中一旦咱們的項目混淆了,這將會致使自動建立的類失效,從而致使咱們自定義的註解失效。因此爲了防止其失效,咱們在這裏再定義一個註解keep:
@Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE) public @interface Keep { }
Retention的做用範圍是在class文件中還要可以被其它class調用,因此這裏使用CLASS;Target做用對象是自動生成的類,因此使用TYPE。至於參數則沒必要要,它只是爲了標明類,防止其被混淆。
butterknife-annotations庫中的自定義註解就完成了。經過上面的分析,咱們注意點主要歸結於如下三點:
註解變量的定義就到這結束了,同時文章中的代碼均可以在Github中獲取到。使用時請將分支切換到feat_annotation_processing
若是感受不錯的話,能夠點贊收藏哦,這是對我最大的支持,謝謝!