您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“Android注解有什么用”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Android注解有什么用”這篇文章吧。
首先什么是注解?@Override
就是注解,它的作用是:
1、檢查是否正確的重寫了父類中的方法。
2、標(biāo)明代碼,這是一個(gè)重寫的方法。
1、體現(xiàn)在于:檢查子類重寫的方法名與參數(shù)類型是否正確;檢查方法private/final/static等不能被重寫。實(shí)際上@Override
對(duì)于應(yīng)用程序并沒有實(shí)際影響,從它的源碼中可以出來。
2、主要是表現(xiàn)出代碼的可讀性。
作為Android開發(fā)中熟知的注解,Override只是注解的一種體現(xiàn),更多時(shí)候,注解還有以下作用:
降低項(xiàng)目的耦合度。
自動(dòng)完成一些規(guī)律性的代碼。
自動(dòng)生成java代碼,減輕開發(fā)者的工作量。
元注解是由java提供的基礎(chǔ)注解,負(fù)責(zé)注解其它注解,如上圖Override被@Target
和@Retention
修飾,它們用來說明解釋其它注解,位于sdk/sources/android-25/java/lang/annotation
路徑下。
元注解有:
@Retention:注解保留的生命周期
@Target:注解對(duì)象的作用范圍。
@Inherited:@Inherited標(biāo)明所修飾的注解,在所作用的類上,是否可以被繼承。
@Documented:如其名,javadoc的工具文檔化,一般不關(guān)心。
@Retention
Retention說標(biāo)明了注解被生命周期,對(duì)應(yīng)RetentionPolicy的枚舉,表示注解在何時(shí)生效:
SOURCE:只在源碼中有效,編譯時(shí)拋棄,如上面的
@Override
。CLASS:編譯class文件時(shí)生效。
RUNTIME:運(yùn)行時(shí)才生效。
如下圖,com.android.support:support-annotations
中的Nullable注解,會(huì)在編譯期判斷,被注解的參數(shù)是否會(huì)空,具體后續(xù)分析。
@Target
Target標(biāo)明了注解的適用范圍,對(duì)應(yīng)ElementType枚舉,明確了注解的有效范圍。
TYPE:類、接口、枚舉、注解類型。
FIELD:類成員(構(gòu)造方法、方法、成員變量)。
METHOD:方法。
PARAMETER:參數(shù)。
CONSTRUCTOR:構(gòu)造器。
LOCAL_VARIABLE:局部變量。
ANNOTATION_TYPE:注解。
PACKAGE:包聲明。
TYPE_PARAMETER:類型參數(shù)。
TYPE_USE:類型使用聲明。
如上圖所示,@Nullable
可用于注解方法,參數(shù),類成員,注解,包聲明中,常用例子如下所示:
/** * Nullable表明 * bind方法的參數(shù)target和返回值Data可以為null */ @Nullable public static Data bind(@Nullable Context target) { //do someThing and return return bindXXX(target); }
@Inherited
注解所作用的類,在繼承時(shí)默認(rèn)無法繼承父類的注解。除非注解聲明了 @Inherited。同時(shí)Inherited聲明出來的注,只對(duì)類有效,對(duì)方法/屬性無效。
如下方代碼,注解類@AInherited
聲明了Inherited ,而注解BNotInherited 沒有,所在在它們的修飾下:
類Child繼承了父類Parent的
@AInherited
,不繼承@BNotInherited
;重寫的方法
testOverride()
不繼承Parent的任何注解;
testNotOverride()
因?yàn)闆]有被重寫,所以注解依然生效。
@Retention(RetentionPolicy.RUNTIME) @Inherited public @interface AInherited { String value(); } @Retention(RetentionPolicy.RUNTIME) public @interface BNotInherited { String value(); } @AInherited("Inherited") @BNotInherited("沒Inherited") public class Parent { @AInherited("Inherited") @BNotInherited("沒Inherited") public void testOverride(){ } @AInherited("Inherited") @BNotInherited("沒Inherited") public void testNotOverride(){ } } /** * Child繼承了Parent的AInherited注解 * BNotInherited因?yàn)闆]有@Inherited聲明,不能被繼承 */public class Child extends Parent { /** * 重寫的testOverride不繼承任何注解 * 因?yàn)镮nherited不作用在方法上 */ @Override public void testOverride() { } /** * testNotOverride沒有被重寫 * 所以注解AInherited和BNotInherited依然生效。 */}
2.1 運(yùn)行時(shí)注解
了解了元注解后,看看如何實(shí)現(xiàn)和使用自定義注解。這里我們簡單介紹下運(yùn)行時(shí)注解RUNTIME,編譯時(shí)注解CLASS留著后面分析。
首先,創(chuàng)建一個(gè)注解遵循: public @interface 注解名 {方法參數(shù)},如下方@getViewTo
注解:
@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface getViewTo { int value() default -1; }
然后如下方所示,我們將注解描述在Activity的成員變量mTv
和mBtn
中,在App運(yùn)行時(shí),通過反射將findViewbyId得到的控件,注入到mTv
和mBtn
中。
是不是很熟悉,有點(diǎn)ButterKnife的味道?當(dāng)然,ButterKnife比這個(gè)高級(jí)多,畢竟反射多了影響效率,不過我們明白了,可以通過注解來注入和創(chuàng)建對(duì)象,這樣可以在一定程度節(jié)省代碼量。
public class MainActivity extends AppCompatActivity { @getViewTo(R.id.textview) private TextView mTv; @getViewTo(R.id.button) private Button mBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //通過注解生成View; getAllAnnotationView(); } /** * 解析注解,獲取控件 */ private void getAllAnnotationView() { //獲得成員變量 Field[] fields = this.getClass().getDeclaredFields(); for (Field field : fields) { try { //判斷注解 if (field.getAnnotations() != null) { //確定注解類型 if (field.isAnnotationPresent(GetViewTo.class)) { //允許修改反射屬性 field.setAccessible(true); GetViewTo getViewTo = field.getAnnotation(GetViewTo.class); //findViewById將注解的id,找到View注入成員變量中 field.set(this, findViewById(getViewTo.value())); } } } catch (Exception e) { } } } }
2.2 編譯時(shí)注解
運(yùn)行時(shí)注解RUNTIME如上2.1所示,大多數(shù)時(shí)候?qū)嵲谶\(yùn)行時(shí)使用反射來實(shí)現(xiàn)所需效果,這很大程度上影響效率,如果BufferKnife的每個(gè)View注入不可能如何實(shí)現(xiàn)。實(shí)際上,ButterKnife使用的是編譯時(shí)注解CLASS,如下圖X2.2,是ButterKnife的@BindView
注解,它是一個(gè)編譯時(shí)注解,在編譯時(shí)生成對(duì)應(yīng)java代碼,實(shí)現(xiàn)注入。
說到編譯時(shí)注解,就不得不說注解處理器 AbstractProcessor,如果你有注意,一般第三方注解相關(guān)的類庫,如bufferKnike、ARouter,都有一個(gè)Compiler命名的Module,如下圖X2.3,這里面一般都是注解處理器,用于編譯時(shí)處理對(duì)應(yīng)的注解。
注解處理器(Annotation Processor)是javac的一個(gè)工具,它用來在編譯時(shí)掃描和處理注解(Annotation)。你可以對(duì)自定義注解,并注冊相應(yīng)的注解處理器,用于處理你的注解邏輯。
如下所示,實(shí)現(xiàn)一個(gè)自定義注解處理器,至少重寫四個(gè)方法,并且注冊你的自定義Processor,詳細(xì)可參考下方代碼CustomProcessor
。
@AutoService(Processor.class),谷歌提供的自動(dòng)注冊注解,為你生成注冊Processor所需要的格式文件(
com.google.auto
相關(guān)包)。init(ProcessingEnvironment env),初始化處理器,一般在這里獲取我們需要的工具類。
getSupportedAnnotationTypes(),指定注解處理器是注冊給哪個(gè)注解的,返回指定支持的注解類集合。
getSupportedSourceVersion() ,指定java版本。
process(),處理器實(shí)際處理邏輯入口。
@AutoService(Processor.class)public class CustomProcessor extends AbstractProcessor { /** * 注解處理器的初始化 * 一般在這里獲取我們需要的工具類 * @param processingEnvironment 提供工具類Elements, Types和Filer */ @Override public synchronized void init(ProcessingEnvironment env){ super.init(env); //Element代表程序的元素,例如包、類、方法。 mElementUtils = env.getElementUtils(); //處理TypeMirror的工具類,用于取類信息 mTypeUtils = env.getTypeUtils(); //Filer可以創(chuàng)建文件 mFiler = env.getFiler(); //錯(cuò)誤處理工具 mMessages = env.getMessager(); } /** * 處理器實(shí)際處理邏輯入口 * @param set * @param roundEnvironment 所有注解的集合 * @return */ @Override public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { //do someThing } //指定注解處理器是注冊給哪個(gè)注解的,返回指定支持的注解類集合。 @Override public Set<String> getSupportedAnnotationTypes() { Set<String> sets = new LinkedHashSet<String>(); //大部分class而已getName、getCanonicalNam這兩個(gè)方法沒有什么不同的。 //但是對(duì)于array或內(nèi)部類等就不一樣了。 //getName返回的是[[Ljava.lang.String之類的表現(xiàn)形式, //getCanonicalName返回的就是跟我們聲明類似的形式。 sets(BindView.class.getCanonicalName()); return sets; } //指定Java版本,一般返回最新版本即可 @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } }
首先,我們梳理下一般處理器處理邏輯:
1、遍歷得到源碼中,需要解析的元素列表。
2、判斷元素是否可見和符合要求。
3、組織數(shù)據(jù)結(jié)構(gòu)得到輸出類參數(shù)。
4、輸入生成java文件。
5、錯(cuò)誤處理。
然后,讓我們理解一個(gè)概念:Element
,因?yàn)樗俏覀儷@取注解的基礎(chǔ)。
Processor處理過程中,會(huì)掃描全部Java源碼,代碼的每一個(gè)部分都是一個(gè)特定類型的Element,它們像是XML一層的層級(jí)機(jī)構(gòu),比如類、變量、方法等,每個(gè)Element代表一個(gè)靜態(tài)的、語言級(jí)別的構(gòu)件,如下方代碼所示。
package android.demo; // PackageElement// TypeElementpublic class DemoClass { // VariableElement private boolean mVariableType; // VariableElement private VariableClassE m VariableClassE; // ExecuteableElement public DemoClass () { } // ExecuteableElement public void resolveData (Demo data //TypeElement ) { } }
其中,Element
代表的是源代碼,而TypeElement
代表的是源代碼中的類型元素,例如類。然而,TypeElement
并不包含類本身的信息。你可以從TypeElement
中獲取類的名字,但是你獲取不到類的信息,例如它的父類。這種信息需要通過TypeMirror
獲取。你可以通過調(diào)用elements.asType()
獲取元素的TypeMirror
。
1、知道了Element
,我們就可以通過process 中的RoundEnvironment
去獲取,掃描到的所有元素,如下圖X2.4,通過env.getElementsAnnotatedWith
,我們可以獲取被@BindView注解的元素的列表,其中validateElement
校驗(yàn)元素是否可用。
2、因?yàn)?code>env.getElementsAnnotatedWith返回的,是所有被注解了@ BindView的元素的列表。所以有時(shí)候我們還需要走一些額外的判斷,比如,檢查這些Element是否是一個(gè)類:
@Override public boolean process(Set<? extends TypeElement> an, RoundEnvironment env) { for (Element e : env.getElementsAnnotatedWith(BindView.class)) { // 檢查元素是否是一個(gè)類 if (ae.getKind() != ElementKind.CLASS) { ... } } ... }
3、javapoet (com.squareup:javapoet
)是一個(gè)根據(jù)指定參數(shù),生成java文件的開源庫,有興趣了解javapoet的可以看下javapoet——讓你從重復(fù)無聊的代碼中解放出來,在處理器中,按照參數(shù)創(chuàng)建出 JavaFile
之后,通Filer
利用javaFile.writeTo(filer);
就可以生成你需要的java文件。
4、錯(cuò)誤處理,在處理器中,我們不能直接拋出一個(gè)異常,因?yàn)樵趐rocess()中拋出一個(gè)異常,會(huì)導(dǎo)致運(yùn)行注解處理器的JVM崩潰,導(dǎo)致跟蹤棧信息十分混亂。因此,注解處理器就有一個(gè)Messager類,一般通過messager.printMessage( Diagnostic.Kind.ERROR, StringMessage, element)
即可正常輸出錯(cuò)誤信息。
至此,你的注解處理器完成了所有的邏輯。可以看出,編譯時(shí)注解實(shí)在編譯時(shí)生成java文件,然后將生產(chǎn)的java文件注入到源碼中,在運(yùn)行時(shí)并不會(huì)像運(yùn)行時(shí)注解一樣,影響效率和資源。
以上是“Android注解有什么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。