注解支持(Support Annotations)
Android support library從19.1版本開始引入了一個(gè)新的注解庫籽御,它包含很多有用的元注解绘盟,你能用它們修飾你的代碼,幫助你發(fā)現(xiàn)bug程奠。Support library自己本身也用到了這些注解,所以作為support library的用戶霹粥,Android Studio已經(jīng)基于這些注解校驗(yàn)了你的代碼并且標(biāo)注其中潛在的問題忆蚀。Support library 22.2版本又新增了13個(gè)新的注解以供使用欣鳖。
使用注解庫
注解默認(rèn)是沒有包含的;他們被包裝成一個(gè)獨(dú)立的庫矾缓。(support library現(xiàn)在由一些更小的庫組成:v4-support, appcompat, gridlayout, mediarouter等等)
(如果你正在使用appcompat庫样眠,那么你已經(jīng)可以使用這些注解了盟戏,因?yàn)閍ppcomat它自己也依賴它。)
添加使用注解最簡單的方式就是打開Project Structure對話框。首先在左邊選中module问窃,然后在右邊選中Dependencies標(biāo)簽頁亥鬓,點(diǎn)擊面板底部的+按鈕,選擇Library Dependency域庇,假設(shè)你已經(jīng)把Android Support Repository安裝到你的SDK中了嵌戈,那么注解庫將會(huì)出現(xiàn)在列表中,你只需點(diǎn)擊選中它即可(這里是列表中的第一個(gè))
添加依賴
點(diǎn)擊OK完成Project Structure的編輯听皿。這會(huì)修改你的build.gradle文件熟呛,當(dāng)然你也可以手動(dòng)編輯它:
dependencies { compile 'com.android.support:support-annotations:22.2.0'}
對于Android application和Android library這兩個(gè)類型的module(你應(yīng)用了com.android.application或者com.android.library插件的)來說,你需要做的已經(jīng)都做好了尉姨。如果你想只在Java module使用這些注解庵朝,那么你就明確的包含SDK倉庫了,因?yàn)閟upport libraries不能從jcenter獲得(Android Gradle插件會(huì)自動(dòng)的包含這些依賴啊送,但是Java插件卻沒有偿短。)
repositories {
jcenter()
maven {
url '<your-SDK-path>/extras/android/m2repository'
}
}
執(zhí)行注解
當(dāng)你用Android Studio和IntelliJ的時(shí)候,如果給標(biāo)注了這些注解的方法傳遞錯(cuò)誤類型的參數(shù)馋没,那么IDE就會(huì)實(shí)時(shí)標(biāo)記出來昔逗。
從Gradle插件1.3.0-beta1版本開始,并且安裝了Android M Preview平臺(tái)工具的情況下篷朵,通過命令行調(diào)用gradle的lint
任務(wù)就可以執(zhí)行這些檢查勾怒。如果你想把標(biāo)記問題作為持續(xù)集成的一部分婆排,那么這種方式是非常有用的。說明:這并不包含nullness注解笔链。本文中所介紹的其他注解都可以通過lint執(zhí)行檢查段只。
Nullness Annotations
@Nullable注解能被用來標(biāo)注給定的參數(shù)或者返回值可以為null。
類似的鉴扫,@NonNull注解能被用來標(biāo)注給定的參數(shù)或者返回值不能為null赞枕。
如果一個(gè)本地變量的值為null(比如因?yàn)檫^早的代碼檢查它是否為null),而你又把它作為參數(shù)傳遞給了一個(gè)方法坪创,并且該方法的參數(shù)又被@NonNull標(biāo)注炕婶,那么IDE會(huì)提醒你,你有一個(gè)潛在的崩潰問題莱预。
v4 support library中的FragmentActivity的示例代碼:
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
...
/**
* Add support for inflating the <fragment> tag.
*/
@Nullable
@Override
public View onCreateView(String name, @NonNull Context context, @NonNull AttributeSet attrs) {
...
(如果你執(zhí)行Analyze > Infer Nullity柠掂,或者你在鍵入時(shí)把@NonNull替換成了@NotNull,那么IDE可能會(huì)提供附加的IntelliJ注解依沮。參考底部的“IntelliJ Annotations”段落了解更多)
注意@NonNull和@Nullable并不是對立的:還有第三種可能:未指定涯贞。當(dāng)你沒有指定@NonNull或者@Nullable的時(shí)候,工具就不能確定危喉,所以這個(gè)API也就不起作用宋渔。
最初,我們在findViewById方法上標(biāo)注@Nullable姥饰,從技術(shù)上說傻谁,這是正確的:findViewById可以返回null。但是如果你知道你在做什么的時(shí)候(如果你傳遞給他一個(gè)存在的id)他是不會(huì)返回null的列粪。當(dāng)我們使用@Nullable注解它的時(shí)候审磁,就意味著源代碼編輯器中會(huì)有大量的代碼出現(xiàn)高亮警告。如果你已經(jīng)意識(shí)到每次使用該方法都應(yīng)該明確的進(jìn)行null檢查岂座,那么就只能用@Nullable標(biāo)注返回值态蒂。有個(gè)經(jīng)驗(yàn)規(guī)則:看現(xiàn)有的“好的代碼”(比如審查產(chǎn)品代碼),看看這些API是怎么被使用的费什。如果該代碼為null檢查結(jié)果钾恢,你應(yīng)該為方法注解@Nullable。
資源類型注解
Android的資源值通常都是使用整型傳遞鸳址。這意味著獲取一個(gè)drawable使用的參數(shù)瘩蚪,也能很容易的傳遞給一個(gè)獲取string的方法;因?yàn)樗麄兌际莍nt類型稿黍,編譯器很難區(qū)分疹瘦。
資源類型注解可以在這種情況下提供類型檢查。比如一個(gè)被@StringRes住進(jìn)誒的int類型參數(shù)巡球,如果傳遞一個(gè)不是R.string類型的引用將會(huì)被IDE標(biāo)注:
資源類型注解以ActionBar為例:
import android.support.annotation.StringRes;
...public abstract void setTitle(@StringRes int resId);
有`很多不同資源類型的注解:如下的每一個(gè)Android資源類型:
@StringRes, @DrawableRes, @ColorRes, @InterpolatorRes,等等言沐。一般情況下邓嘹,如果有一個(gè)foo
類型的資源,那么它的相應(yīng)的資源類型注解就是FooRes.
除此之外险胰,還有一個(gè)名為@AnyRes特殊的資源類型注解汹押。它被用來標(biāo)注一個(gè)未知的特殊類型的資源,但是它必須是一個(gè)資源類型起便。比如在框架中棚贾,它被用在Resources#getResourceName(@AnyRes int resId)
上,使用的時(shí)候缨睡,你可以這樣getResources().getResourceName(R.drawable.icon)
用鸟悴,也可以getResources().getResourceName(R.string.app_name)
這樣用陈辱,但是卻不能這樣getResources().getResourceName(42)
用奖年。
請注意,如果你的API支持多個(gè)資源類型沛贪,你可以使用多個(gè)注解來標(biāo)注你的參數(shù)陋守。
IntDef/StringDef: 類型定義注解
整型除了可以作為資源的引用之外,也可以用作“枚舉”類型使用利赋。
@IntDef和”typedef”作用非常類似水评,你可以創(chuàng)建另外一個(gè)注解,然后用@IntDef指定一個(gè)你期望的整型常量值列表媚送,最后你就可以用這個(gè)定義好的注解修飾你的API了中燥。
appcompat庫里的一個(gè)例子:
import android.support.annotation.IntDef;
...
public abstract class ActionBar {
...
@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
@Retention(RetentionPolicy.SOURCE)
public @interface NavigationMode {}
public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;
@NavigationMode
public abstract int getNavigationMode();
public abstract void setNavigationMode(@NavigationMode int mode);
上面非注解的部分是現(xiàn)有的API。我們創(chuàng)建了一個(gè)新的注解(NavigationMode)并且用@IntDef標(biāo)注它,通過@IntDef我們?yōu)榉祷刂祷蛘邊?shù)指定了可用的常量值塘偎。我們還添加了@Retention(RetentionPolicy.SOURCE)
告訴編譯器這個(gè)新定義的注解不需要被記錄在生成的.class文件中(譯者注:源代碼級別的疗涉,生成class文件的時(shí)候這個(gè)注解就被編譯器自動(dòng)去掉了)。
使用這個(gè)注解后吟秩,如果你傳遞的參數(shù)或者返回值不在指定的常量值中的話咱扣,IDE將會(huì)標(biāo)記出這種情況。
你也可以指定一個(gè)整型是一個(gè)標(biāo)記性質(zhì)的類型涵防;這樣客戶端代碼就通過|闹伪,&等操作符同時(shí)傳遞多個(gè)常量了:
@IntDef(flag=true, value={
DISPLAY_USE_LOGO,
DISPLAY_SHOW_HOME,
DISPLAY_HOME_AS_UP,
DISPLAY_SHOW_TITLE,
DISPLAY_SHOW_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayOptions {}
最后,還有一個(gè)字符串版本的注解壮池,就是@StringDef偏瓤,它和@IntDef的作用基本上是一樣,所不同的是它是針對字符串的椰憋。該注解一般不常用厅克,但是有的時(shí)候非常有用,比如在限定向Activity#getSystemService方法傳遞的參數(shù)范圍的時(shí)候熏矿。
要了解關(guān)于類型注解的更多詳細(xì)信息已骇,請參考https://developer.android.com/tools/debugging/annotations.html#enum-annotations
(這些都是建立在IntelliJ’s MagicConstant注解的基礎(chǔ)上离钝,你可以在這里找到該注解的詳細(xì)信息:http://blog.jetbrains.com/idea/2012/02/new-magic-constant-inspection/)