Android-Annotation教你寫自定義註解

一 前言

我相信註解咱們多多少少的都會接觸到,經常使用的框架Butterknife、Retrofit、ARouter等等都用到了註解,我想你們都會去搜一下什麼是註解了吧。這裏呢就以一個Demo去了解一下自定義註解的使用。java

二 知識準備

咱們最多見的註解莫過於@Override了吧,那咱們就去看一下這個註解的代碼。git

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}複製代碼

好了就事論事咱們先看下聲明這個@interface乍一看還覺得是interface呢不過這裏多了個@那就是聲明註解的關鍵字了,這隻要記住就行了。大括號裏面居然沒任何的定義,那就先放一放,咱們來看一下其餘的參數。github

  • @Target()
    @Target說明了Annotation所修飾的對象範圍,也就是咱們這個註解是用在那個對象上面的:Annotation可被用於 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。
    做用:用於描述註解的使用範圍(即:被描述的註解能夠用在什麼地方)取值(ElementType)是來源於Java.lang.annotation.ElementType中的枚舉類型元素:
       (1).CONSTRUCTOR:用於描述構造器
       (2).FIELD:用於描述域
       (3).LOCAL_VARIABLE:用於描述局部變量
       (4).METHOD:用於描述方法
       (5).PACKAGE:用於描述包
       (6).PARAMETER:用於描述參數
       (7).TYPE:用於描述類、接口(包括註解類型) 或enum聲明
  • @Retention()
    @Retention定義了該Annotation被保留的時間長短:某些Annotation僅出如今源代碼中,而被編譯器丟棄;而另外一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另外一些在class被裝載時將被讀取(請注意並不影響class的執行,由於Annotation與class在使用上是被分離的)。使用這個meta-Annotation能夠對 Annotation的「生命週期」限制。
    做用:表示須要在什麼級別保存該註釋信息,用於描述註解的生命週期(即:被描述的註解在什麼範圍內有效)取值(RetentionPoicy)來源於java.lang.annotation.RetentionPolicy的枚舉類型值:
       (1).SOURCE:在源文件中有效(即源文件保留)
       (2).CLASS:在class文件中有效(即class保留)
       (3).RUNTIME:在運行時有效(即運行時保留)
  • @Documented
    @Documented用於描述其它類型的annotation應該被做爲被標註的程序成員的公共API,所以能夠被例如javadoc此類的工具文檔化。Documented是一個標記註解,沒有成員。
  • @Inherited:
    @Inherited 元註解是一個標記註解,@Inherited闡述了某個被標註的類型是被繼承的。若是一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。 注意:@Inherited annotation類型是被標註過的class的子類所繼承。類並不從它所實現的接口繼承annotation,方法並不從它所重載的方法繼承annotation。當@Inherited annotation類型標註的annotation的Retention是RetentionPolicy.RUNTIME,則反射API加強了這種繼承性。若是咱們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工做:檢查class和其父類,直到發現指定的annotation類型被發現,或者到達類繼承結構的頂層。
這裏呢大概的講了一下定義在類上面的每一個註解的意義和取值是什麼,下面咱們將進入咱們的自定義註解中。

三 自定義註解

咱們先來照葫蘆畫瓢,定義一個註解類數組

public @interface MyTag {

}複製代碼

註解裏面的定義也是有規定的:bash

  • 註解方法不能帶有參數。框架

    • 註解方法返回值類型限定爲:基本類型、String、Enums、Annotation或者這些類型的數組。
  • 註解方法能夠有默認值。ide

  • 註解自己可以包含元註解,元註解被用來註解其餘註解。工具

咱們就來試一下吧!ui

public @interface MyTag {
  //聲明返回值類型,這裏可沒有大括號啊,能夠設置默認返回值,而後就直接";"了啊。
    String name () default "" ;

    int size () default 0 ;

}複製代碼

定義好了註解咱們就來規定咱們這個註解要用到哪裏什麼時候用吧!this

@Target({ElementType.METHOD,ElementType.FIELD})
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTag {
    String name () default "" ;
    int size () default 0 ;
}複製代碼

這裏呢咱們定義這個註解能夠用在屬性和方法上面,是可繼承的註解,能夠出如今運行時的。由於咱們這邊要模仿一下一下其餘註解框架中註解的用法,我這裏才採用了RetentionPolicy.RUNTIME,由於在運行時咱們採用反射能夠獲得裏面的註解信息。
好了接下來看怎麼使用咱們的這個自定義的註解!

public class HomeActivity extends AppCompatActivity {
    @MyTag(name = "BMW",size = 100)
    Car car;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        //這裏咱們要首先註冊一下這個類
        AnnotationCar.instance().inject(this);
      //當程序運行的時候這裏將會輸出該類Car的屬性值。
        Log.e("WANG","Car is "+car.toString());
    }
}複製代碼

是否是很像咱們使用過的ButterKnife呢,這裏呢咱們再這個Activity裏面定義了一個Car類的屬性,而後再car這個變量上面定義咱們的註解,而且給咱們的註解賦值。而後咱們再onCreate方法裏面先初始化咱們的註解,而後打印Car類的信息,先來看下結果吧

cn.example.wang.routerdemo E/WANG: Car is Car [name=BMW, size=100]複製代碼

這樣咱們的自定義註解就有做用了,好了半天主要的代碼就在那個初始化裏面。

//自定義的類
/**
 * Created by WANG on 17/11/21.
 */

public class AnnotationCar {
    private static AnnotationCar annotationCar;
    public static AnnotationCar instance(){
        synchronized (AnnotationCar.class){
            if(annotationCar == null){
                annotationCar = new AnnotationCar();
            }
            return annotationCar;
        }
    }

    public void inject(Object o){
        Class<?> aClass = o.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field:declaredFields) {
            if(field.getName().equals("car") && field.isAnnotationPresent(MyTag.class)) {
                MyTag annotation = field.getAnnotation(MyTag.class);
                Class<?> type = field.getType();
                if(Car.class.equals(type)) {
                    try {
                        field.setAccessible(true);
                        field.set(o, new Car(annotation.name(), annotation.size()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}複製代碼

這就說明了爲何註解和反射是同時進入咱們的知識圈裏面的吧!這裏呢咱們先獲取到類裏面全部的屬性,而後去找到被咱們的註解MyTag修飾的那個屬性,而後找到以後,先取咱們註解裏面的值,而後賦值給咱們類裏面的屬性!這樣咱們就用註解去初始化了一個屬性值,嘻嘻這裏就結束了啊!

結束語

例子很簡單,看完以後是否是也會寫一個相似ButterKnife的效果了呢,有什麼問題請留言給我,我會實時爲你解答的!

感受對你有幫助請點個贊啊~
歡迎關注 個人掘金
歡迎關注 個人簡書
歡迎關注 個人csdn
源碼下載

相關文章
相關標籤/搜索