Android注解學(xué)習(xí)筆記

最近在看一些開(kāi)源項(xiàng)目的源碼捕透,發(fā)現(xiàn)了Android中的一些很有意思的注解聪姿,于是歸納總結(jié)了一下,以后在自己的項(xiàng)目中也可以嘗試使用乙嘀。

首先末购,需要在gradle的dependencies中加入
compile 'com.android.support:support-annotations:25.2.0'
當(dāng)前的最新版本是25.2.0

Android注解有8種類(lèi)型,分別是Nullness注解虎谢、資源類(lèi)型注解盟榴、線(xiàn)程注解、變量限制注解婴噩、權(quán)限注解曹货、結(jié)果檢查注解、CallSuper注解讳推、枚舉注解顶籽。

Nullness注解

  • @NonNull
@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, ANNOTATION_TYPE, PACKAGE})
public @interface NonNull {
}
  • @Nullable
@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, ANNOTATION_TYPE, PACKAGE})
public @interface Nullable {
}

從名字上就可以很明顯的看出,@NonNull表示這個(gè)參數(shù)银觅、變量等不能為空礼饱,而@Nullable則表示可以為空,舉個(gè)例子:

    private void test(@NonNull String test) {
        
    }

如果我們有這樣的一個(gè)函數(shù)究驴,用@NonNull注解表示參數(shù)不能為空镊绪,如果我們這樣調(diào)用這個(gè)函數(shù)

    test(null);

我們會(huì)得到這樣的警告提示,告訴我們這個(gè)參數(shù)被注解為@NonNull


@NonNull

如果我們將這個(gè)函數(shù)改為:

    private void testNonNull(@Nullable String test) {
        
    }

或者沒(méi)有任何注解時(shí)洒忧,就沒(méi)有提示了蝴韭。

資源類(lèi)型注解

所有以“Res”結(jié)尾的注解,都是資源類(lèi)型注解熙侍。大概包括:@AnimatorRes榄鉴、@AnimRes履磨、@AnyRes、@ArrayRes庆尘、@AttrRes剃诅、@BoolRes、@ColorRes驶忌、@DimenRes矛辕、@DrawableRes、@FractionRes付魔、@IdRes聊品、@IntRes、@IntegerRes几苍、@InterpolatorRes翻屈、@LayoutRes、@MenuRes擦剑、@PluralsRes妖胀、@RawRes芥颈、@StringRes惠勒、@StyleableRes、@StyleRes爬坑、@TransitionRes纠屋、@XmlRes

使用方法也都是類(lèi)似的,這里舉個(gè)例子:

@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
public @interface StringRes {
}
    private String getStringById(@StringRes int stringId) {
        return getResources().getString(stringId);
    }

如果我們這樣寫(xiě)String name = getStringById(R.string.app_name);是不會(huì)有問(wèn)題的盾计,但是團(tuán)隊(duì)中的其他小伙伴在調(diào)用的時(shí)候?qū)戝e(cuò)了怎么辦售担?比如String name = getStringById(R.layout.activity_main);如果@StringRes注解,我們看不到任何的提醒署辉,而運(yùn)行時(shí)就會(huì)報(bào)錯(cuò)族铆,但是如果加上了@StringRes注解,我們就可以看到這樣的錯(cuò)誤提示:

@StringRes

線(xiàn)程注解

包括@AnyThread哭尝、@UiThread和@WorkerThread哥攘,表明需要運(yùn)行在什么線(xiàn)程上。

@Documented
@Retention(CLASS)
@Target({METHOD,CONSTRUCTOR,TYPE})
public @interface WorkerThread {
}

例如有一個(gè)函數(shù)材鹦,需要做一些耗時(shí)操作逝淹,我們希望它不要運(yùn)行在主線(xiàn)程上

    @WorkerThread
    private void testThread() {
        // 耗時(shí)操作
    }

如果我們?cè)谥骶€(xiàn)程中調(diào)用這個(gè)函數(shù)會(huì)怎么樣呢?


@WorkerThread

而如果這樣調(diào)用就不會(huì)有問(wèn)題桶唐,這樣就保證了我們這個(gè)耗時(shí)操作不會(huì)執(zhí)行在主線(xiàn)程中栅葡。

        new Thread(new Runnable() {
            public void run() {
                testThread();
            }
        }).start();

變量限制注解

變量限制注解主要包含@IntRange和@FloatRange兩種,使用方法類(lèi)似尤泽,都是限制了范圍欣簇,這里以@IntRange為例规脸。

@Retention(CLASS)
@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
public @interface IntRange {
    /** Smallest value, inclusive */
    long from() default Long.MIN_VALUE;
    /** Largest value, inclusive */
    long to() default Long.MAX_VALUE;
}

源碼中可以看到,這里包含了from()to()醉蚁,默認(rèn)值分別是Long的最小值Long.MIN_VALUE和Long的最大值Long.MAX_VALUE燃辖。
例如我們有個(gè)方法,需要限制輸入的范圍网棍,我可以這樣寫(xiě):

    private void testRange(@IntRange(from = 1, to = 10) int number) {
        
    }

如果調(diào)用者輸入了一個(gè)超出范圍的值時(shí)黔龟,會(huì)這樣提示他。


@IntRange

權(quán)限注解

如果我們有方法需要使用某種權(quán)限滥玷,可以加上@RequiresPermission這個(gè)注解氏身。

    @RequiresPermission(Manifest.permission.CALL_PHONE)
    private void testPermission() {

    }

比如這里需要打電話(huà)的權(quán)限,但是我并沒(méi)有在應(yīng)用中加入該權(quán)限惑畴。


沒(méi)有CALL_PHONE權(quán)限

當(dāng)我們調(diào)用函數(shù)時(shí)蛋欣,就會(huì)有這樣的錯(cuò)誤提示。好吧如贷,那我把權(quán)限加上陷虎,發(fā)現(xiàn)還是有錯(cuò)誤提示。


加入了CALL_PHONE權(quán)限

沒(méi)有錯(cuò)杠袱,AS就是這么強(qiáng)大尚猿,會(huì)告訴我們這個(gè)權(quán)限可能會(huì)被用戶(hù)拒絕,所以我們應(yīng)該在代碼中對(duì)這個(gè)權(quán)限進(jìn)行檢查楣富。
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        testPermission();

這樣就沒(méi)有問(wèn)題了凿掂。

結(jié)果檢查注解

如果我們寫(xiě)了一個(gè)有返回值的函數(shù),并且我們希望調(diào)用者對(duì)這個(gè)返回值進(jìn)行使用或者檢查纹蝴。這個(gè)時(shí)候@CheckResult注解就派上用場(chǎng)了庄萎。

@Documented
@Retention(CLASS)
@Target({METHOD})
public @interface CheckResult {
    /** Defines the name of the suggested method to use instead, if applicable (using
     * the same signature format as javadoc.) If there is more than one possibility,
     * list them all separated by commas.
     * <p>
     * For example, ProcessBuilder has a method named {@code redirectErrorStream()}
     * which sounds like it might redirect the error stream. It does not. It's just
     * a getter which returns whether the process builder will redirect the error stream,
     * and to actually set it, you must call {@code redirectErrorStream(boolean)}.
     * In that case, the method should be defined like this:
     * <pre>
     *  @CheckResult(suggest="#redirectErrorStream(boolean)")
     *  public boolean redirectErrorStream() { ... }
     * </pre>
     */
    String suggest() default "";
}

比如有這樣一個(gè)方法,返回了String塘安。

    @CheckResult
    private boolean testCheckResult() {
        return true;
    }

如果我們不關(guān)心他的返回值糠涛。

@CheckResult

提示我們結(jié)果沒(méi)有被使用。如果改為boolean result = testCheckResult();就不會(huì)有問(wèn)題了兼犯。@CheckResult注解保證了我們方法的返回值一定會(huì)得到使用忍捡。

CallSuper注解

如果我們有一個(gè)父類(lèi)Father,有一個(gè)方法display()免都,有一個(gè)子類(lèi)Child繼承了Father锉罐,并重寫(xiě)了display()方法,并不會(huì)有任何問(wèn)題绕娘。

class Father {
        public void display() {
            Log.i(TAG, "display: Father");
        }
    }

    class Child extends Father {
        @Override
        public void display() {
            Log.i(TAG, "display: Child");
        }
    }

但是脓规,如果我想讓子類(lèi)Child在調(diào)用display()方式也將父類(lèi)Father的某些信息一起打印出來(lái)怎么辦?沒(méi)錯(cuò)险领,在子類(lèi)的display()方法中調(diào)用super.display();即可侨舆。那么秒紧,我們?cè)趺幢WC子類(lèi)就一定會(huì)調(diào)用super的方法呢?@CallSuper注解登場(chǎng)挨下。

@Documented
@Retention(CLASS)
@Target({METHOD})
public @interface CallSuper {
}

@CallSuper注解表示重寫(xiě)的方法必須調(diào)用父類(lèi)的方法熔恢,注意,這里的Target只有METHOD臭笆,并沒(méi)有CONSTRUCTOR叙淌,所以構(gòu)造函數(shù)是不能使用這個(gè)注解的。
還是剛才的例子愁铺,如果我們?cè)诟割?lèi)的方法上加上@CallSuper注解鹰霍,這個(gè)時(shí)候子類(lèi)中重寫(xiě)的方法就會(huì)提示這樣的錯(cuò)誤。


@CallSuper

這樣就提醒我們需要加上super的方法茵乱。

枚舉注解

Android官方強(qiáng)烈建議不要在Android程序里面使用到enum茂洒,官方的Training課程里面有下面這樣一句話(huà):Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.

既然官方都這樣說(shuō)了,那就盡量不要使用枚舉了瓶竭,可是不使用枚舉使用什么呢督勺?沒(méi)錯(cuò),靜態(tài)常量斤贰。
舉個(gè)例子智哀,比如我自己寫(xiě)了一個(gè)提示框,需要提示用戶(hù)一些信息腋舌,所以這樣寫(xiě):

public class MyTip {
    public static void show(String message) {
        // 顯示提示框
    }
}

我希望這個(gè)提示框在顯示一定時(shí)間后自動(dòng)關(guān)掉盏触,所以定義了兩個(gè)靜態(tài)常量渗蟹,一個(gè)長(zhǎng)時(shí)間一個(gè)短時(shí)間块饺,并且作為show方法的一個(gè)參數(shù)。

public class MyTip {
    public static final int LONG_TIME = 0;
    public static final int SHORT_TIME = 1;
    
    public static void show(String message, int type) {
        // 顯示提示框
    }
}

我可以這樣讓我的提示框顯示一個(gè)較長(zhǎng)的時(shí)間蛾派。

MyTip.show("message", MyTip.LONG_TIME);

但是有個(gè)問(wèn)題读第,這里我傳入的參數(shù)是MyTip.LONG_TIME们妥,但是實(shí)際上不管傳入的是1還是0,甚至是MyTip.show("message", 2);都不會(huì)提示錯(cuò)誤淮腾,因?yàn)橹灰莍nt就可以,這顯示不是我想要的屉佳。這里我們就需要用到枚舉注解了谷朝,@IntDef和@StringDef

@Retention(SOURCE)
@Target({ANNOTATION_TYPE})
public @interface IntDef {
    /** Defines the allowed constants for this element */
    long[] value() default {};

    /** Defines whether the constants can be used as a flag, or just as an enum (the default) */
    boolean flag() default false;
}

這時(shí)候我再修改一下代碼

public class MyTip {
    public static final int LONG_TIME = 0;
    public static final int SHORT_TIME = 1;

    @IntDef({LONG_TIME, SHORT_TIME})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Type {}

    public static void show(String message, @Type int type) {
        // 顯示提示框
    }
}

這里自己寫(xiě)了一個(gè)注解@Type,并且使用了@IntDef注解武花,value就是兩個(gè)靜態(tài)常量圆凰。這時(shí)候再看調(diào)用的地方。


@IntDef

是不是非常熟悉体箕?沒(méi)錯(cuò)专钉,我們熟悉的Toast就是用@IntDef注解這么實(shí)現(xiàn)的挑童。感興趣的可以去看看源碼。

總結(jié)

發(fā)現(xiàn)注解是一個(gè)非常有意思的東西跃须,他可以讓我們?cè)诤茉绲臅r(shí)候就發(fā)現(xiàn)問(wèn)題站叼,團(tuán)隊(duì)協(xié)作起來(lái)也更有效率,所以做了一些總結(jié)菇民,希望以后在項(xiàng)目中能夠多多用到注解尽楔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市第练,隨后出現(xiàn)的幾起案子翔试,更是在濱河造成了極大的恐慌,老刑警劉巖复旬,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垦缅,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡驹碍,警方通過(guò)查閱死者的電腦和手機(jī)壁涎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)志秃,“玉大人怔球,你說(shuō)我怎么就攤上這事「』梗” “怎么了竟坛?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)钧舌。 經(jīng)常有香客問(wèn)我担汤,道長(zhǎng),這世上最難降的妖魔是什么洼冻? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任崭歧,我火速辦了婚禮,結(jié)果婚禮上撞牢,老公的妹妹穿的比我還像新娘率碾。我一直安慰自己,他們只是感情好屋彪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布所宰。 她就那樣靜靜地躺著,像睡著了一般畜挥。 火紅的嫁衣襯著肌膚如雪仔粥。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天砰嘁,我揣著相機(jī)與錄音件炉,去河邊找鬼勘究。 笑死,一個(gè)胖子當(dāng)著我的面吹牛斟冕,可吹牛的內(nèi)容都是我干的口糕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼磕蛇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼景描!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起秀撇,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤超棺,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后呵燕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體棠绘,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年再扭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了氧苍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泛范,死狀恐怖让虐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情罢荡,我是刑警寧澤赡突,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站区赵,受9級(jí)特大地震影響惭缰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惧笛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一从媚、第九天 我趴在偏房一處隱蔽的房頂上張望逞泄。 院中可真熱鬧患整,春花似錦、人聲如沸喷众。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)到千。三九已至昌渤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間憔四,已是汗流浹背膀息。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工般眉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人潜支。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓甸赃,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親冗酿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子埠对,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,110評(píng)論 25 707
  • 原文轉(zhuǎn)載地址:http://www.linuxidc.com/Linux/2015-08/121993.htm 作...
    Android技術(shù)研究閱讀 1,974評(píng)論 0 8
  • 什么是注解 注解對(duì)于開(kāi)發(fā)人員來(lái)講既熟悉又陌生,熟悉是因?yàn)橹灰闶亲鲩_(kāi)發(fā)裁替,都會(huì)用到注解(常見(jiàn)的@Override)项玛;...
    張明云閱讀 25,163評(píng)論 6 154
  • 男生永遠(yuǎn)不懂女生為何那么喜歡逛街拍照襟沮,就像女生永遠(yuǎn)不懂男生為何那么癡迷打游戲一樣。男生眼中女生的自拍還停留在:“磨...
    镸大包閱讀 575評(píng)論 0 0
  • 1 根據(jù)人員需求的專(zhuān)業(yè)和語(yǔ)言情況昌腰,東北地區(qū)都是每年校招必去的地方臣嚣,除了傳統(tǒng)的強(qiáng)勢(shì)機(jī)械專(zhuān)業(yè)院校,如東北大學(xué)剥哑、大連理工...
    冬冬Steven閱讀 161評(píng)論 0 1