摘要:下面我們會(huì)自己實(shí)現(xiàn)與注解,實(shí)現(xiàn)中的對(duì)應(yīng)注解功能。帶大家一起來(lái)聲明注解變量。知道了它的作用范圍之后,我們?cè)谧远x注解時(shí)就要盡量較小注解的作用范圍,提高項(xiàng)目的編譯與運(yùn)行速度。它們代表自定義的注解能夠作用的對(duì)象。總結(jié)庫(kù)中的自定義注解就完成了。
對(duì)于Android注解,或多或少都有一點(diǎn)接觸,但相信大多數(shù)人都是在使用其它依賴庫(kù)的時(shí)候接觸的。因?yàn)橛行?kù)如果你想使用它就必須使用它所提供的注解。例如:ButterKnife、Dagger2、Room等等。
至于為何使用注解?使用過(guò)的應(yīng)該都知道,最明顯的就是方便、簡(jiǎn)潔。通過(guò)使用注解可以在項(xiàng)目編譯階段,幫助我們自動(dòng)生成一些重復(fù)的代碼,減輕我們的負(fù)擔(dān)。典型的ButterKnife本質(zhì)就是使用Android注解,通過(guò)注解來(lái)減少我們對(duì)view.findViewById的編寫(xiě),提高我們的開(kāi)發(fā)效率。上一個(gè)系列(AAC)的Room也是一樣,我們可以簡(jiǎn)單的回顧一下:
@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 )
通過(guò)使用注解來(lái)定義一個(gè)實(shí)體表,也就10行左右的代碼。如果要我們?nèi)孔约簩?xiě)那絕對(duì)要兩三百行代碼了,而且其中還可能出錯(cuò),又要改bug等等。效率就嚴(yán)重降低。對(duì)于依賴庫(kù)如果都這么麻煩也就不會(huì)有人用了。
那么如何判斷一個(gè)依賴庫(kù)是否需要使用注解呢?其實(shí)很簡(jiǎn)單,只要記住以下兩點(diǎn)即可:
需要生成的代碼不能與項(xiàng)目邏輯有關(guān)
Android注解只能生成代碼,并不能修改代碼
這里透露一下,Android注解的本質(zhì)是使用Java的反射機(jī)制,后續(xù)會(huì)詳細(xì)說(shuō)明項(xiàng)目架構(gòu)
相信ButterKnife應(yīng)該有接觸過(guò)吧,沒(méi)有的也沒(méi)關(guān)系,現(xiàn)在正是時(shí)候。下面我們會(huì)自己實(shí)現(xiàn)BindView與OnClick注解,實(shí)現(xiàn)ButterKnife中的對(duì)應(yīng)注解功能。那么我先來(lái)看下整體的項(xiàng)目架構(gòu)
通過(guò)項(xiàng)目圖,我們可以清晰的看到,主要分為三個(gè)部分
butterknife-annotations:注解庫(kù),包含BindView與OnClick等自定義的注解
butterknife-bind:綁定庫(kù),自定義的注解與聲明的類綁定
butterknife-compiler: 解析編譯生成庫(kù),解析聲明類中的注解,在編譯時(shí)自動(dòng)生成相應(yīng)的代碼。
為了幫助大家能夠更輕松的理解Android注解,今天主要分析的就是butterknife-annotations這個(gè)注解庫(kù)。帶大家一起來(lái)聲明注解變量。
BindView為了要實(shí)現(xiàn)開(kāi)源庫(kù)butterknife類似的綁定id效果,這里我們先定義一個(gè)BindView注解,具體如下:
@Retention(RetentionPolicy.SOURCE) @Target(ElementType.FIELD) public @interface BindView { @IdRes int[] value(); }
嗯,還是很簡(jiǎn)單的對(duì)吧。也就5行代碼解決BindView注解的定義。
那么再來(lái)詳細(xì)剖析這5行代碼。
Retention首先是第一行代碼的Retention,看它的使用方式就能知道,它也是一個(gè)聲明了的注解。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ RetentionPolicy value(); }
通過(guò)源碼我們可以看出該注解只接收一個(gè)參數(shù),該參數(shù)為RetentionPolicy類型。那么我們?cè)谶M(jìn)一步深入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 }
在這里我們發(fā)現(xiàn)它其實(shí)是一個(gè)枚舉,在枚舉中支持三個(gè)常量,分別為SOURCE、CLASS與RUNTIME。它們的區(qū)別主要是作用的周期范圍,下面我再對(duì)這三個(gè)的作用進(jìn)行翻譯一遍:
SOURCE: 使用該標(biāo)明的注解將在編譯階段就被拋棄掉。
CLASS:使用該標(biāo)明的注解將在編譯階段記錄到生成的class文件中,但在運(yùn)行階段時(shí)又會(huì)被VM拋棄。默認(rèn)是該模式。
RUNTIME:使用該標(biāo)明的注解將在編譯階段被保存在生成的class文件中,同時(shí)在運(yùn)行階段時(shí)會(huì)保存到VM中。所以它該注解將一直存在,自然能夠通過(guò)java的反射機(jī)制進(jìn)行讀取。
所以它們的存在的生命時(shí)長(zhǎng)為SOURCE < CLASS < RUNTIME。知道了它的作用范圍之后,我們?cè)谧远x注解時(shí)就要盡量較小注解的作用范圍,提高項(xiàng)目的編譯與運(yùn)行速度。
因?yàn)槲覀兊腂indView注解只是為了進(jìn)行Viwe的綁定,所以在編譯之后就無(wú)需存在,所以這里就使用了CLASS來(lái)進(jìn)行標(biāo)明。
Target下面我們?cè)趤?lái)看第二行代碼,這里使用到了另一個(gè)注解Target,我們還是來(lái)看下它的源碼:
@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(); }
可以看到注解的源碼都非常簡(jiǎn)單,這里接收了一個(gè)ElementType數(shù)組參數(shù),ElementType不難猜出它的類型也是一個(gè)枚舉:
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常量,但我們實(shí)際真正常用的也就是前面8種。它們代表自定義的注解能夠作用的對(duì)象。分別為:
TYPE: 作用于類、接口或者枚舉
FIELD:作用于類中聲明的字段或者枚舉中的常量
METHOD:作用于方法的聲明語(yǔ)句中
PARAMETER:作用于參數(shù)聲明語(yǔ)句中
CONSTRUCTOR:作用于構(gòu)造函數(shù)的聲明語(yǔ)句中
LOCAL_VARIABLE:作用于局部變量的聲明語(yǔ)句中
ANNOTATION_TYPE:作用于注解的聲明語(yǔ)句中
PACKAGE:作用于包的聲明語(yǔ)句中
TYPE_PARAMETER:java 1.8之后,作用于類型聲明的語(yǔ)句中
TYPE_USE:java 1.8之后,作用于使用類型的任意語(yǔ)句中
結(jié)合我們的BindView的作用是對(duì)View進(jìn)行id綁定,自然是作用與聲明的字段上。所以在BindView中使用了FIELD。
再來(lái)看第四行代碼
@IdRes int[] value()
有了上面的注解接觸,不難理解這是標(biāo)明BindView將接收一個(gè)int類型的數(shù)組參數(shù)。對(duì)于開(kāi)源庫(kù)butterknife中的BindView是接收需要綁定的View的id,這里我們做一個(gè)改版,再接收一個(gè)String的id,用來(lái)為綁定的View設(shè)置默認(rèn)值。這樣我們自定義了的BindView注釋就完成了。
OnClick下面我們?cè)僮远x一個(gè)OnClick點(diǎn)擊的注解,經(jīng)過(guò)上面的分析,可以在腦海中想想Retention與Target分別什么值?
想好了之后我們?cè)趤?lái)過(guò)一遍
@Retention(RetentionPolicy.SOURCE) @Target(ElementType.METHOD) public @interface OnClick { @IdRes int value(); }
Retention的作用范圍與BindView一樣首頁(yè)SOURCE,在編譯之后就無(wú)需存在;Target的作用對(duì)象與BindView不同,既然是點(diǎn)擊事件的點(diǎn)擊操作,自然是作用在操作邏輯的方法上,所以這里使用METHOD。
keep文章開(kāi)頭有提及到本質(zhì)是通過(guò)注解來(lái)自動(dòng)生成代碼,為我們創(chuàng)建所需的類,那么在實(shí)際開(kāi)發(fā)中一旦我們的項(xiàng)目混淆了,這將會(huì)導(dǎo)致自動(dòng)創(chuàng)建的類失效,從而導(dǎo)致我們自定義的注解失效。所以為了防止其失效,我們?cè)谶@里再定義一個(gè)注解keep:
@Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE) public @interface Keep { }
Retention的作用范圍是在class文件中還要能夠被其它c(diǎn)lass調(diào)用,所以這里使用CLASS;Target作用對(duì)象是自動(dòng)生成的類,所以使用TYPE。至于參數(shù)則不必要,它只是為了標(biāo)明類,防止其被混淆。
總結(jié)butterknife-annotations庫(kù)中的自定義注解就完成了。通過(guò)上面的分析,我們注意點(diǎn)主要?dú)w結(jié)于以下三點(diǎn):
Retention: 明確注解作用的生命時(shí)長(zhǎng),盡早的消除
Target: 明確注解作用的對(duì)象
Keep: 防止后續(xù)自動(dòng)生成的類被混淆
注解變量的定義就到這結(jié)束了,同時(shí)文章中的代碼都可以在Github中獲取到。使用時(shí)請(qǐng)將分支切換到feat_annotation_processing
如果感覺(jué)不錯(cuò)的話,可以點(diǎn)贊收藏哦,這是對(duì)我最大的支持,謝謝!
相關(guān)文章自定義Android注解Part2:代碼自動(dòng)生成
自定義Android注解Part3:綁定
關(guān)注私人博客
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/71463.html
摘要:上一節(jié)我們已經(jīng)將自動(dòng)生成注解代碼部分介紹完畢,今天這篇文章是自定義注解系列的最后一篇文章。該部分是對(duì)我們前面定義的注解變量與自動(dòng)生成的代碼進(jìn)行綁定,即調(diào)用我們自動(dòng)生成的代碼。 showImg(https://segmentfault.com/img/bVbdEsh?w=1200&h=720); 上一節(jié)我們已經(jīng)將自動(dòng)生成注解代碼部分介紹完畢,今天這篇文章是自定義Android注解系列的最...
摘要:在中是主流布局方式。它有三種狀態(tài)正數(shù)零與負(fù)數(shù)。來(lái)看下運(yùn)行效果。這是為正數(shù)的情況,如果,控件的大小就會(huì)根據(jù)設(shè)置的與來(lái)固定顯示。如果發(fā)現(xiàn)生效的方式請(qǐng)務(wù)必告知。在中有主軸與副軸之分,主軸控制的排列方向,默認(rèn)為。默認(rèn)值為,繼承父容器的屬性。 今天我們來(lái)聊聊Flexbox,它是前端的一個(gè)布局方式。在React Native中是主流布局方式。如果你剛剛?cè)腴T(mén)React Native,或者沒(méi)有多少前端...
閱讀 1275·2021-09-27 13:35
閱讀 2575·2021-09-06 15:12
閱讀 3392·2019-08-30 15:55
閱讀 2841·2019-08-30 15:43
閱讀 442·2019-08-29 16:42
閱讀 3454·2019-08-29 15:39
閱讀 3073·2019-08-29 12:28
閱讀 1251·2019-08-29 11:11