注解支持(Support Annotations)
Android support library從19.1版本開始引入了一個新的注解庫,它包含很多有用的元注解,你能用它們修飾你的代碼,幫助你發現bug。Support library自己本身也用到了這些注解,所以作為support library的用戶,Android Studio已經基于這些注解校驗了你的代碼并且標注其中潛在的問題。Support library 22.2版本又新增了13個新的注解以供使用。
使用注解庫
注解默認是沒有包含的;他們被包裝成一個獨立的庫。(support library現在由一些更小的庫組成:v4-support, appcompat, gridlayout, mediarouter等等)
(如果你正在使用appcompat庫,那么你已經可以使用這些注解了,因為appcomat它自己也依賴它。)
添加使用注解最簡單的方式就是打開Project Structure對話框。首先在左邊選中module,然后在右邊選中Dependencies標簽頁,點擊面板底部的+按鈕,選擇Library Dependency,假設你已經把Android Support Repository安裝到你的SDK中了,那么注解庫將會出現在列表中,你只需點擊選中它即可(這里是列表中的第一個)
添加依賴
點擊OK完成Project Structure的編輯。這會修改你的build.gradle文件,當然你也可以手動編輯它:
dependencies { compile 'com.android.support:support-annotations:22.2.0'}
對于Android application和Android library這兩個類型的module(你應用了com.android.application或者com.android.library插件的)來說,你需要做的已經都做好了。如果你想只在Java module使用這些注解,那么你就明確的包含SDK倉庫了,因為support libraries不能從jcenter獲得(Android Gradle插件會自動的包含這些依賴,但是Java插件卻沒有。)
repositories {
jcenter()
maven {
url '<your-SDK-path>/extras/android/m2repository'
}
}
執行注解
當你用Android Studio和IntelliJ的時候,如果給標注了這些注解的方法傳遞錯誤類型的參數,那么IDE就會實時標記出來。
從Gradle插件1.3.0-beta1版本開始,并且安裝了Android M Preview平臺工具的情況下,通過命令行調用gradle的lint
任務就可以執行這些檢查。如果你想把標記問題作為持續集成的一部分,那么這種方式是非常有用的。說明:這并不包含nullness注解。本文中所介紹的其他注解都可以通過lint執行檢查。
Nullness Annotations
@Nullable注解能被用來標注給定的參數或者返回值可以為null。
類似的,@NonNull注解能被用來標注給定的參數或者返回值不能為null。
如果一個本地變量的值為null(比如因為過早的代碼檢查它是否為null),而你又把它作為參數傳遞給了一個方法,并且該方法的參數又被@NonNull標注,那么IDE會提醒你,你有一個潛在的崩潰問題。
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) {
...
(如果你執行Analyze > Infer Nullity,或者你在鍵入時把@NonNull替換成了@NotNull,那么IDE可能會提供附加的IntelliJ注解。參考底部的“IntelliJ Annotations”段落了解更多)
注意@NonNull和@Nullable并不是對立的:還有第三種可能:未指定。當你沒有指定@NonNull或者@Nullable的時候,工具就不能確定,所以這個API也就不起作用。
最初,我們在findViewById方法上標注@Nullable,從技術上說,這是正確的:findViewById可以返回null。但是如果你知道你在做什么的時候(如果你傳遞給他一個存在的id)他是不會返回null的。當我們使用@Nullable注解它的時候,就意味著源代碼編輯器中會有大量的代碼出現高亮警告。如果你已經意識到每次使用該方法都應該明確的進行null檢查,那么就只能用@Nullable標注返回值。有個經驗規則:看現有的“好的代碼”(比如審查產品代碼),看看這些API是怎么被使用的。如果該代碼為null檢查結果,你應該為方法注解@Nullable。
資源類型注解
Android的資源值通常都是使用整型傳遞。這意味著獲取一個drawable使用的參數,也能很容易的傳遞給一個獲取string的方法;因為他們都是int類型,編譯器很難區分。
資源類型注解可以在這種情況下提供類型檢查。比如一個被@StringRes住進誒的int類型參數,如果傳遞一個不是R.string類型的引用將會被IDE標注:
資源類型注解以ActionBar為例:
import android.support.annotation.StringRes;
...public abstract void setTitle(@StringRes int resId);
有`很多不同資源類型的注解:如下的每一個Android資源類型:
@StringRes, @DrawableRes, @ColorRes, @InterpolatorRes,等等。一般情況下,如果有一個foo
類型的資源,那么它的相應的資源類型注解就是FooRes.
除此之外,還有一個名為@AnyRes特殊的資源類型注解。它被用來標注一個未知的特殊類型的資源,但是它必須是一個資源類型。比如在框架中,它被用在Resources#getResourceName(@AnyRes int resId)
上,使用的時候,你可以這樣getResources().getResourceName(R.drawable.icon)
用,也可以getResources().getResourceName(R.string.app_name)
這樣用,但是卻不能這樣getResources().getResourceName(42)
用。
請注意,如果你的API支持多個資源類型,你可以使用多個注解來標注你的參數。
IntDef/StringDef: 類型定義注解
整型除了可以作為資源的引用之外,也可以用作“枚舉”類型使用。
@IntDef和”typedef”作用非常類似,你可以創建另外一個注解,然后用@IntDef指定一個你期望的整型常量值列表,最后你就可以用這個定義好的注解修飾你的API了。
appcompat庫里的一個例子:
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);
上面非注解的部分是現有的API。我們創建了一個新的注解(NavigationMode)并且用@IntDef標注它,通過@IntDef我們為返回值或者參數指定了可用的常量值。我們還添加了@Retention(RetentionPolicy.SOURCE)
告訴編譯器這個新定義的注解不需要被記錄在生成的.class文件中(譯者注:源代碼級別的,生成class文件的時候這個注解就被編譯器自動去掉了)。
使用這個注解后,如果你傳遞的參數或者返回值不在指定的常量值中的話,IDE將會標記出這種情況。
你也可以指定一個整型是一個標記性質的類型;這樣客戶端代碼就通過|,&等操作符同時傳遞多個常量了:
@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 {}
最后,還有一個字符串版本的注解,就是@StringDef,它和@IntDef的作用基本上是一樣,所不同的是它是針對字符串的。該注解一般不常用,但是有的時候非常有用,比如在限定向Activity#getSystemService方法傳遞的參數范圍的時候。
要了解關于類型注解的更多詳細信息,請參考https://developer.android.com/tools/debugging/annotations.html#enum-annotations
(這些都是建立在IntelliJ’s MagicConstant注解的基礎上,你可以在這里找到該注解的詳細信息:http://blog.jetbrains.com/idea/2012/02/new-magic-constant-inspection/)