Android 自定義注解

??我們在項(xiàng)目中經(jīng)常用到注解侄旬,比如原生自帶的@Override、@NonNull等 盔粹,等三方框架ButterKnife中@BindView、@OnClick等程癌。用的久了舷嗡,就想著去看看它是怎么弄出來的,因自定義注解的使用場景比較少嵌莉,故此次咱先做下簡單了解进萄。

注解的作用:
1.和編譯器一起給你一些提示警告信息。
2.配合一些ide 可以更加方便快捷 安全有效的編寫java代碼。谷歌出的support-annotations這個庫 就是主要干這個的垮斯。
3.和反射一起 提供一些類似于spring 可配置的功能郎仆,方便簡潔。

運(yùn)行時注解與編譯時注解的區(qū)別是什么呢兜蠕?
a)保留階段不同扰肌。運(yùn)行時注解保留到運(yùn)行時,可在運(yùn)行時訪問熊杨。而編譯時注解保留到編譯時曙旭,運(yùn)行時無法訪問。
b)原理不同晶府。運(yùn)行時注解是Java反射機(jī)制桂躏,而編譯時注解通過APT、AbstractProcessor川陆。
c)性能不同剂习。運(yùn)行時注解由于使用Java反射,因此對性能上有影響较沪。編譯時注解對性能沒影響鳞绕。這也是為什么ButterKnife從運(yùn)行時切換到了編譯時的原因。
d)產(chǎn)物不同尸曼。運(yùn)行時注解只需自定義注解處理器即可们何,不會產(chǎn)生其他文件。而編譯時注解通常會產(chǎn)生新的Java源文件控轿。

??下面來試試實(shí)現(xiàn)ButterKnife中的BindView和OnClick冤竹。
先定義下BindView(ViewInject)和OnClick的注解,寫的是運(yùn)行時注解:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Retention 用于聲明該注解生效的生命周期茬射,有三個枚舉值可以選擇<br>
 * 1. RetentionPolicy.SOURCE 注釋只保留在源碼上面鹦蠕,編譯成class的時候自動被編譯器抹除
 * 2. RetentionPolicy.CLASS 注釋只保留到字節(jié)碼上面,VM加載字節(jié)碼時自動抹除
 * 3. RetentionPolicy.RUNTIME 注釋永久保留躲株,可以被VM加載時加載到內(nèi)存中
 * 注意:由于我們的目的是想在VM運(yùn)行時對Filed上的該注解進(jìn)行反射操作片部,因此Retention值必須設(shè)置為RUNTIME
 * @Target 用于指定該注解可以聲明在哪些成員上面,常見的值有FIELD和Method霜定,
 * 由于我們的當(dāng)前注解類是想聲明在Filed上面
 * 因此這里設(shè)置為ElementType.FIELD档悠。
 * 注意:如果@Target值不設(shè)置,則默認(rèn)可以添加到任何元素上望浩,不推薦這么寫辖所。
 * @interface 是聲明注解類的組合關(guān)鍵字。
 */

@Target({java.lang.annotation.ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {//通過@interface 來定義注解
    public abstract int value();
}
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({java.lang.annotation.ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick
{
    public abstract int[] value();
}

定義好注解后磨德,再寫個的類去處理findViewById和OnClick的操作:

import android.app.Activity;
import android.view.View;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ViewUtilsTest {
    public static void inject(final Activity activity) {
        /**
         * 通過字節(jié)碼獲取activity類中所有的字段缘回,在獲取Field的時候一定要使用
         * getDeclaredFields(),
         * 因?yàn)橹挥性摲椒ú拍塬@取到任何權(quán)限修飾的Filed吆视,包括私有的。
         */
        Class clazz = activity.getClass();
        Field[] declaredFields = clazz.getDeclaredFields();
        //一個Activity中可能有多個Field酥宴,因此遍歷啦吧。
        for (int i = 0; i < declaredFields.length; i++) {
            Field field = declaredFields[i];
            //設(shè)置為可訪問,暴力反射拙寡,就算是私有的也能訪問到
            field.setAccessible(true);
            //獲取到字段上面的注解對象
            ViewInject annotation = (ViewInject) field.getAnnotation(ViewInject.class);
            //一定對annotation是否等于null進(jìn)行判斷授滓,因?yàn)椴⒉皇撬蠪iled上都有我們想要的注解
            if (annotation == null) {
                continue;
            }
            //獲取注解中的值
            int id = annotation.value();
            //獲取控件
            View view = activity.findViewById(id);
            try {
                //將該控件設(shè)置給field對象
                field.set(activity, view);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        //獲取所有的方法(私有方法也可以獲取到)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (int i = 0; i < declaredMethods.length; i++) {
            final Method method = declaredMethods[i];
            //獲取方法上面的注解
            OnClick annotation = (OnClick) method.getAnnotation(OnClick.class);
            if (annotation == null) {
                //如果該方法上沒有注解,循環(huán)下一個
                continue;
            }
            //獲取注解中的數(shù)據(jù)肆糕,因?yàn)榭梢越o多個button綁定點(diǎn)擊事件般堆,因此定義注解類時使用的是int[]作為數(shù)據(jù)類型。
            int[] value = annotation.value();
            for (int j = 0; j < value.length; j++) {
                int id = value[j];
                final View button = activity.findViewById(id);
                button.setOnClickListener(new View.OnClickListener() {
                    public void onClick(View v) {
                        try {
                            //反射調(diào)用用戶指定的方法
                            method.invoke(activity, button);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    }
}

代碼中的意思很容易看懂诚啃,就是把界面activity傳過來淮摔,再去遍歷定義好的字段,取出注入的id始赎,最后仍然是咱們經(jīng)常用的findViewById和橙。點(diǎn)擊監(jiān)聽也是如此。然后极阅,就結(jié)束了胃碾。。就可以用了筋搏,像ButterKnife一樣用就行:

public class TestActivity extends AppCompatActivity {
    @ViewInject(R.id.btn_test)
    Button button;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test1);
        ViewUtilsTest.inject(this);
    }

    @OnClick(R.id.btn_test)
    public void onClick(View v) {
        if (v.getId() == R.id.btn_test) {
            Toast.makeText(TestActivity.this,"test111111",Toast.LENGTH_SHORT).show();
        }
    }    
}

貌似沒問題了,可以成功的toast出來厕隧。但這只是最簡單的了解奔脐,想再多了解點(diǎn)的,可以看看這個:http://www.reibang.com/p/806e3500fec4

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吁讨,一起剝皮案震驚了整個濱河市髓迎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌建丧,老刑警劉巖排龄,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異翎朱,居然都是意外死亡橄维,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門拴曲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來争舞,“玉大人,你說我怎么就攤上這事澈灼【捍ǎ” “怎么了店溢?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長委乌。 經(jīng)常有香客問我床牧,道長,這世上最難降的妖魔是什么遭贸? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任戈咳,我火速辦了婚禮,結(jié)果婚禮上革砸,老公的妹妹穿的比我還像新娘除秀。我一直安慰自己,他們只是感情好算利,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布册踩。 她就那樣靜靜地躺著,像睡著了一般效拭。 火紅的嫁衣襯著肌膚如雪暂吉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天缎患,我揣著相機(jī)與錄音慕的,去河邊找鬼。 笑死挤渔,一個胖子當(dāng)著我的面吹牛肮街,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播判导,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嫉父,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了眼刃?” 一聲冷哼從身側(cè)響起绕辖,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎擂红,沒想到半個月后仪际,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昵骤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年树碱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涉茧。...
    茶點(diǎn)故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡赴恨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伴栓,到底是詐尸還是另有隱情伦连,我是刑警寧澤雨饺,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站惑淳,受9級特大地震影響额港,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜歧焦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一移斩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绢馍,春花似錦向瓷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瓷耙,卻和暖如春朱躺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搁痛。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工长搀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鸡典。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓源请,卻偏偏與公主長得像,于是被迫代替她去往敵國和親彻况。 傳聞我的和親對象是個殘疾皇子巢钓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評論 2 355

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