Android注解-什么蟋软?啥是注解?干啥的嗽桩?

首先帶入問題岳守。
1.什么是注解。
2.注解有什么用碌冶,我們?yōu)槭裁匆米⒔猓?/h5>
3.注解的生命周期湿痢,編譯時(shí)注解和運(yùn)行時(shí)注解區(qū)別。

引用別人對(duì)注解的解釋,注解可以理解成標(biāo)簽譬重。

在代碼中我們最常見的注解應(yīng)該是他 @Override 拒逮,用于重載父類的方法。
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

點(diǎn)擊 @Override 進(jìn)到源碼臀规,中我們會(huì)發(fā)現(xiàn)下面這種結(jié)構(gòu)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

那么滩援,參考源碼的實(shí)現(xiàn)方式我們這樣就可以生成自己的注解

public @interface TestAnnotation {

}

在我們代碼中進(jìn)行調(diào)用

@TestAnnotation
private void logOut() {
    Log.i(TAG, "logOut: ");
}

只不過這種注解實(shí)現(xiàn),除了能增加代碼量塔嬉,其他毫無意義 [手動(dòng)狗頭]玩徊。

上面@Override 注解中出現(xiàn)的 @Target 和 @Retention

這兩個(gè)東西,一看感覺就是注解的參數(shù)邑遏,點(diǎn)進(jìn)源碼會(huì)發(fā)現(xiàn)官方給他們起了一個(gè)專門的名字佣赖,meta-annotation(元注解),其只能记盒,表明聲明的類型憎蛤,僅用作復(fù)雜注釋類型聲明中的成員類型。它不能用于直接注釋任何內(nèi)容纪吮。(意思對(duì)注解進(jìn)行注解)

/**
* <p>This {@code @Target} meta-annotation indicates that the declared type is
* intended solely for use as a member type in complex annotation type
* declarations.  It cannot be used to annotate anything directly:
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

如果我們?cè)谄渌牡胤秸{(diào)用俩檬,as會(huì)彈出類似的報(bào)錯(cuò)。


TargetError.png

元注解是注解中的一種碾盟,他通過 @Target(ElementType.ANNOTATION_TYPE) 指定其使用場(chǎng)景棚辽,只能用在注解上,即對(duì)注解進(jìn)行注解
Target 支持的類型

ElementType 類型范圍
TYPE 類冰肴、接口(包括注釋類型)或枚舉聲明
FIELD 字段聲明(包括枚舉常量
METHOD 方法
PARAMETER 參數(shù)
CONSTRUCTOR 構(gòu)造方法
LOCAL_VARIABLE 局部變量
ANNOTATION_TYPE 注解
PACKAGE
TYPE_PARAMETER 類型參數(shù)(泛型)
TYPE_USE 使用類型注解

@Retention 指定注解的生命周期

SOURCE CLASS RUNTIME
生命周期 源碼階段 Class文件階段 運(yùn)行
解釋 .java文件(僅在我們開發(fā)過程中存在,
提示錯(cuò)誤或警告)
編譯后從java變成.class文件
保存在字節(jié)碼
ClassLoder 加載class字節(jié)碼到內(nèi)存

那么對(duì)第一個(gè)問題的總結(jié) 什么是注解屈藐,在代碼層面上,只要使用@interface 的都是注解熙尉。如果我們指定他的Target在annotation上联逻。@Target(ElementType.ANNOTATION_TYPE) 那么也可以稱他為元注解。



2 注解的作用
  • @Retention(RetentionPolicy.SOURCE)
    源代碼時(shí)期的注解检痰,僅存在于.java文件中
    1.用于代碼檢查例如@NonNull /@Nullable 包归,資源引用限制例如@DrawableRes,@StringRes铅歼,提醒錯(cuò)誤公壤,過時(shí)例如@Deprecated。
    2.在編譯期間處理椎椰,.java文件編譯生成class文件時(shí)期厦幅,通過開發(fā)者注冊(cè)的注解處理器(AnnotationProcessor)對(duì)注解進(jìn)行處理,使用JavaPoet生成模板代碼慨飘,提高開發(fā)效率确憨。
  • @Retention(RetentionPolicy.RUNTIME)
    1.注解在class字節(jié)碼文件中存在,通過反射獲取到注解的對(duì)象以及參數(shù)等信息提供調(diào)用。容易實(shí)現(xiàn)但反射消耗性能不建議過度使用缚态。

舉個(gè)栗子
完善一下我們之前的TestAnnotation 添加兩個(gè)參數(shù),name 和 age

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    String name();
    int age();
}

在創(chuàng)建一個(gè)UserBean類

public class UserBean {

    private String name;

    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    private String getName() {
        return name;
    }

    @FunctionAnnotation
    private void setName(@FunctionType String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{"
                + "name='" + name + '\''
                + ", age=" + age
                + '}';
    }
}

創(chuàng)建用于綁定的Utils

public class AnnotationUtils {

    public static void Inject(Activity activity) {

        //萬物皆對(duì)象。獲取activity的class
        Class<? extends Activity> c = activity.getClass();

        // 獲取所有字段
        Field[] fields = c.getDeclaredFields();

        //遍歷拿到帶有 @TestAnnotation 注解的字段
        for (Field field : fields) {
            if (field.isAnnotationPresent(TestAnnotation.class)) {
                TestAnnotation annotation = field.getAnnotation(TestAnnotation.class);
                if (annotation == null) {
                    break;
                }
                //TestAnnotation name
                String name = annotation.name();
                int old = annotation.age();

                //類型的包名路徑 例如:com.example.demo.data.UserBean
                String packName = field.getType().getName();
                //手動(dòng)定義的對(duì)象名稱堤瘤,此例子使用的是mUser
                String fieldName = field.getName();

                try {

                    //創(chuàng)建一個(gè)user 這里使用包名路徑
                    Class<?> fieldClass = Class.forName(packName);
                    UserBean user = (UserBean) fieldClass.newInstance();

                    //返回此Class對(duì)象對(duì)應(yīng)類的玫芦、帶指定形參列表的方法
                    Method declaredMethod = fieldClass.getDeclaredMethod("setName", String.class);
                    //設(shè)置可以訪問私有權(quán)限
                    declaredMethod.setAccessible(true);
                    //調(diào)用方法
                    declaredMethod.invoke(user, name);

                    Method declaredMethodSetOld = fieldClass.getDeclaredMethod("setAge", int.class);
                    declaredMethodSetOld.setAccessible(true);
                    declaredMethodSetOld.invoke(user, old);

                    //設(shè)置可以訪問私有權(quán)限
                    field.setAccessible(true);
                    //set對(duì)象
                    field.set(activity,user);

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }

            }
        }

    }
}

MainActivity中使用AnnotationUtils注入U(xiǎn)serBean 對(duì)象

public class MainActivity extends Activity {
    
    @TestAnnotation(name = "xiao ming", old = 18)
    private UserBean mUser;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AnnotationUtils.Inject(this);
        System.out.println(" main activity user is " + mUser);
    }

}

運(yùn)行程序我們會(huì)得到如下的日志打印

I/System.out:  main activity user is User{name='xiao ming', age=18}

注解的作用,小結(jié)
emmm本辐,搞了一圈你就給我看這個(gè)桥帆?我直接new一個(gè)對(duì)象set進(jìn)屬性不就完了嗎。搞這么一圈四不四有毛病慎皱。
對(duì)于我們自己寫代碼的話確實(shí)如此老虫。而且代碼業(yè)務(wù)越簡(jiǎn)單/單一,越不需要反射和注解茫多。如果只要實(shí)現(xiàn)一句hello world祈匙,那搞別的操作真是畫蛇添足。

我理解是這樣的

  • 反射 的場(chǎng)景在于天揖,我們需要使用別人的代碼或者android源碼(依賴庫)夺欲。且無法直接修改,或者調(diào)用今膊,別人寫的代碼的情況些阅。迫不得已 我們可以嘗試使用反射去操作修改別人的對(duì)象,或者調(diào)用方法斑唬。

  • 注解嘛市埋,就厲害了。我們需要給別人提供服務(wù)恕刘,或者依賴庫的時(shí)候缤谎。使用者通過使用,我們開發(fā)者定義的注解雪营,按照我們定義的規(guī)則去標(biāo)注使用者的代碼弓千,從而為開發(fā)者實(shí)現(xiàn)某些功能。方便使用者使用献起,和理解洋访。
    例如:retrofit ,我們就是使用者谴餐,按照retrofit開發(fā)者定義好的各種注解姻政,例如@GET,@POST岂嗓,按照retrofit開發(fā)者定義的規(guī)則去標(biāo)注我們的接口方法汁展。開發(fā)者通過讀取我們的注解,幫我們實(shí)現(xiàn)各種邏輯。其他使用注解的框架同理食绿,都是為了使用者便方便調(diào)用侈咕。
    那么如果我們,需要給別人提供服務(wù)器紧,或者我們要寫依賴庫的時(shí)候耀销,就可以考慮是否用注解去實(shí)現(xiàn)了。

  • 如果上面說的铲汪,理解了熊尉。那應(yīng)該也會(huì)理解,為什么上面舉的栗子掌腰,明明只用反射也可以實(shí)習(xí)給user 設(shè)置 name 和 age狰住,卻偏偏要用注解了吧。(先后關(guān)系齿梁,先有我們定義好注解的TestAnnotation注解的規(guī)則催植,后面才有開發(fā)者使用TestAnnotation 注解去標(biāo)注給User使用)。

注解的優(yōu)勢(shì)
  • 上面的栗子運(yùn)行時(shí)期注解士飒,需要用到反射去完成各種操作查邢,好像體現(xiàn)不出注解的優(yōu)勢(shì)。
  • 源碼時(shí)期注解提示安全以及報(bào)錯(cuò)
  • 編譯時(shí)期注解酵幕,在編譯階段可以為我們生成代碼扰藕,實(shí)現(xiàn)各種功能例如早期的 JakeWharton 大神Butterknife ,生成代碼實(shí)現(xiàn)findviewById操作芳撒。以及Goodle HIlt 依賴注入邓深。
    那我們?nèi)绾螌?shí)現(xiàn)編譯時(shí)注解呢。Javapoet為此做出了很大貢獻(xiàn)笔刹。 https://github.com/square/javapoet 具體使用方法請(qǐng)搜索引擎芥备,輸入Javapoet教學(xué) [手動(dòng)狗頭]

對(duì)于第三個(gè)問題,注解的生命周期舌菜,編譯時(shí)注解和運(yùn)行時(shí)注解區(qū)別
你都看到這了萌壳,應(yīng)該有個(gè)大致答案了。

  • 首先聲明周期就不一樣日月,一個(gè)在編譯期class字節(jié)碼階段袱瓮。一個(gè)被ClassLoder 從class字節(jié)碼到內(nèi)存。
  • 編譯時(shí)期注解一般會(huì)生成各種代碼實(shí)現(xiàn)各種操作爱咬。運(yùn)行時(shí)期注解尺借,程序運(yùn)行時(shí)期獲取到你使用的各種注解,例如retrofit 運(yùn)行時(shí)注解 加 動(dòng)態(tài)代理完成網(wǎng)絡(luò)請(qǐng)求的各種操作精拟。
  • //todo以后想到了繼續(xù)補(bǔ)充
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末燎斩,一起剝皮案震驚了整個(gè)濱河市虱歪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌栅表,老刑警劉巖笋鄙,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異怪瓶,居然都是意外死亡局装,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門劳殖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拨脉,你說我怎么就攤上這事哆姻。” “怎么了玫膀?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵矛缨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我帖旨,道長(zhǎng)箕昭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任解阅,我火速辦了婚禮落竹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘货抄。我一直安慰自己述召,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布蟹地。 她就那樣靜靜地躺著积暖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怪与。 梳的紋絲不亂的頭發(fā)上夺刑,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音分别,去河邊找鬼遍愿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛茎杂,可吹牛的內(nèi)容都是我干的错览。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼煌往,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼倾哺!你這毒婦竟也來了轧邪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤羞海,失蹤者是張志新(化名)和其女友劉穎忌愚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體却邓,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硕糊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腊徙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片简十。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖撬腾,靈堂內(nèi)的尸體忽然破棺而出螟蝙,到底是詐尸還是另有隱情,我是刑警寧澤民傻,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布胰默,位于F島的核電站,受9級(jí)特大地震影響漓踢,放射性物質(zhì)發(fā)生泄漏牵署。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一喧半、第九天 我趴在偏房一處隱蔽的房頂上張望奴迅。 院中可真熱鬧,春花似錦挺据、人聲如沸半沽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽者填。三九已至,卻和暖如春做葵,著一層夾襖步出監(jiān)牢的瞬間占哟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工酿矢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留榨乎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓瘫筐,卻偏偏與公主長(zhǎng)得像蜜暑,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子策肝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容