天天看點

Android自定義注解(一)

為什麼要寫這個,因為前段時間看了一下AOP相關的一些内容,然後也是太久沒寫注解,看得有點那啥不順暢,是以想對注解做個總結。

一.JAVA自帶的注解

(1)Override 覆寫

(2)Deprecated 标記過期方法

(3)SuppressWarnings 屏蔽警告

二.自定義注解

1.定義

我這裡寫個demo自定義一個注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface BindView {
    int value() default -1;
}
           

可以看出注解用@interface來标志。

2.元注解

可以看出在上邊的定義中,上面還有兩個注解,這些被稱為元注解,什麼是元注解,簡單來說就是描述注解的注解

還有什麼中繼資料的,元什麼什麼的,這個元其實不太好解釋,我記得之前看過一個中繼資料的描述,英文是 data about data,這個元有這個about的那種感覺

元注解有4種

(1)@Retention 用來描述周期,Retention 有“保持時間”的意思,這個屬性可以選三個值

SOURCE表示隻在源碼中有用,編譯就沒用了。

CLASS表示在編譯中可用,運作就沒用了。

RUNTIME表示運作時可用。

這三種的差別解釋起來很麻煩,總之一搬我們都是使用RUNTIME

(2)@Target 用來描述作用域,Target有目标的意思,你這個注解要給哪個目标修飾,這個屬性可以選以下的值

CONSTRUCTOR用于描述構造器

FIELD用于描述域

LOCAL_VARIABLE用于描述局部變量

METHOD用于描述方法

PACKAGE用于描述包

PARAMETER用于描述參數

這個就是說,你允許把注解寫在什麼地方。這個屬性是可以多選的,比如@Target({ElementType.FIELD, ElementType.METHOD})

(3)@Inherited 描述是否可以為繼承,預設是false

(4)@Documented 描述是否會儲存到 Javadoc 文檔中

一般我們隻會用到前面兩個,是以後面兩個我就不解釋了。

3.注解的值

可以給注解設定值,比如說我上面的代碼,就在注解裡寫了個

int value() default -1;
           

表示在使用注解時需要傳一個整形的值

@BindView(R.id.tv)
TextView textView;
           

我這傳了個R.id.tv就是一個整形的值。隻有一個值的時候,必須以** value()**來命名,然後調用時就直接傳就行。我還是分情況來說吧。

(1)不需要傳值的情況

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface BindView {

}
           

調用

@BindView
TextView textView;
           

(2)傳一個值的情況

就是我上面寫的代碼

(3)傳多個值的情況

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface BindView {
    int age;
    String name;
}
           
@BindView(age = 18, name = "JackMa")
TextView textView;
           

最後,這個default 表示設定預設值的意思。

三.調用注解

上面我們建立出了注解并定義好,然後我們需要調用這個注解,那怎麼調用呢,一般我們調用一個類或接口都是new 出來,但是這東西怎麼看都覺得new不出來吧,是以我們需要用反射來擷取到注解的對象,然後進行調用

我不想去講反射的内容,不然就沒完了。就說個思路,需要用到哪個方法可以去查api。

注解在Java中就是Annotation懂吧,是以在反射中和Annotation相關的方法都是和注解相關的方法。

比如說擷取注解,我就可以調用.getAnnotation(BindView.class)

再比如說我判斷注解存在不,可以調用isAnnotationPresent()

總之你隻要記住注解是Annotation,之後你不管是在反射中還是在哪裡找和注解相關的方法,都先往Annotation這個名詞相關的地方找準沒錯。

如果你不懂反射這裡說再多也沒有。

四.案例

其實說了這麼多,到底哪裡需要使用到注解呢,如果不使用到,講它有啥用,我個人是目前接觸到AOP涉及注解比較頻繁,其它時候,比如說ButterKnife,Dagger,Retrofit等等這些主流的架構都會用到注解,包括java背景,我接觸過spring會涉及到IOC啊AOP啊這些思想用java來實作也是用到注解比較多,是以可以來寫個小demo試試。

不如就仿照ButterKnife吧,我之前沒看過ButterKnife的源碼啊,我就按照我學會的注解的知識和ButterKnife的調用方法來仿寫個ButterKnife

1.首先定義注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface BindView {
    int value() default -1;
}
           

定義注解,要求傳個資源進來,預設是-1

2.按照ButterKnife的調用的樣子來寫調用的地方
public class MainActivity extends AppCompatActivity {

    @BindView(R.id.tv)
    TextView textView;
    @BindView(R.id.btn)
    Button btn;

    String yyy = "aasdasdas";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        textView.setText("wwwwwwwwwwwwww");
        btn.setText("btn");
    }
}
           

發現ButterKnife調用的地方使用這 ButterKnife.bind(this); 那我就推測findViewById就在這個ButterKnife類的bind方法中進行。

3.定義ButterKnife
public class ButterKnife {

    public static void bind(Activity activity){
        Class<?> cls = activity.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            BindView bindView = field.getAnnotation(BindView.class);
            if (bindView != null) {
                int ids = bindView.value();
                try {
                    field.set(activity, activity.findViewById(ids));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
           

我這裡就用了反射來操作,調用bind方法後,先用cls.getDeclaredFields();擷取所有的全局變量,然後再對每個變量用field.getAnnotation(BindView.class);來擷取這個變量上面的注解,如果存在的話 int ids = bindView.value(); 來擷取我們傳入的資源,最後調用findViewById

field.set(activity, activity.findViewById(ids));
           

很簡單吧,三個類這樣就能實作了ButterKnife的效果

Android自定義注解(一)

image.png

注意判空一定要寫,即使你全部的變量都用了注解,但是如果你DeBug看看,你會發現,變量中不僅僅隻有你定義的,還存在其他的。

其實上面都是瞎寫的,ButterKnife的原理根本不是這樣,雖然我沒看過源碼,但我聽别人說過ButterKnife的實作并不是這樣的,我這的demo隻是仿照了這個功能,具體ButterKnife的原理以後如果有時間我會單獨寫。

通過上面的講解就能簡單的實作注解,我也順便複習加總結了一遍。

繼續閱讀