Android Support 包之一的 support-annotations是通過靜態編譯檢測來提高代碼質量的一個注解工具。里面包含了 Android 開發中常用的代碼檢測注解,幫助開發者提高代碼質量。通過 SDK Manager下載 Android Support Repository 后,在 Gradle 中通過如下聲明來使用該注解包:
dependencies {
compile ‘com.android.support:support-annotations:22.2.0’
}
該工具包含如下幾種類型的代碼檢測:
檢測參數或者返回值是否可以為 null
@Nullable 和 @NonNull 會分別檢測一個變量、參數或者函數返回值是否為 null。如果一個函數的參數用 @NonNull 注解,當調用該函數指定該參數為 null 的時候,代碼檢測工具(Lint)會告訴你一個警告,該參數不能為 null。而 @Nullable 則表示可以為 null。例如 如下的代碼表示 onCreateView 函數的返回值不為 null, 參數 context 和 attrs 也不能為 null:
import android.support.annotation.NonNull;
…
/** Add support for inflating the tag. */
@NonNull
@Override
public View onCreateView(String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
…
}
…
資源類型注解
Android 開發中經常使用各種資源常量 R.XXX 來引用各種資源。例如 圖片資源和字符串資源。這些常量都是 int 類型的,在代碼檢測的時候沒法判斷引用的資源是否有錯誤,比如本來需要一個字符串資源,結果在代碼寫的時候用了一個顏色資源,這種情況只有通過測試才能發現,有些極端情況可能測試也不容易發現。資源類型注解就是為了解決該問題的,資源注解包含如下幾種:
@AnimatorRes 表明該參數、變量或者函數返回值應該是一個 Animator 類型的資源
@AnimRes 表明該參數、變量或者函數返回值應該是一個 Anim 類型的資源
@AnyRes 表明該參數、變量或者函數返回值應該是一個任意類型的資源
@ArrayRes 表明該參數、變量或者函數返回值應該是一個 Array 類型的資源
@AttrRes 表明該參數、變量或者函數返回值應該是一個 attribute 類型的資源
@BoolRes 表明該參數、變量或者函數返回值應該是一個布爾類型的資源
@ColorInt 表明該參數、變量或者函數返回值應該是一個顏色值而不是顏色資源引用,例如應該是一個 AARRGGBB 的整數值。
@ColorRes 表明該參數、變量或者函數返回值應該是一個 color 類型的資源,而不是顏色值。注意和 ColorInt 區別
@DimenRes 表明該參數、變量或者函數返回值應該是一個 dimension 類型的資源
@DrawableRes 表明該參數、變量或者函數返回值應該是一個 drawable 類型的資源
@FractionRes 表明該參數、變量或者函數返回值應該是一個 fraction 類型的資源
@IdRes 表明該參數、變量或者函數返回值應該是一個資源的 ID 類型
@IntegerRes 表明該參數、變量或者函數返回值應該是一個整數類型的資源
@InterpolatorRes 表明該參數、變量或者函數返回值應該是一個 interpolator 類型的資源
@LayoutRes 表明該參數、變量或者函數返回值應該是一個 layout 布局文件類型的資源
@MenuRes 表明該參數、變量或者函數返回值應該是一個 menu 類型的資源
@PluralsRes 表明該參數、變量或者函數返回值應該是一個 plurals 類型的資源
@RawRes 表明該參數、變量或者函數返回值應該是一個 raw 類型的資源
@StringRes 表明該參數、變量或者函數返回值應該是一個字符串類型的資源
@StyleableRes 表明該參數、變量或者函數返回值應該是一個 styleable 類型的資源
@StyleRes 表明該參數、變量或者函數返回值應該是一個 style 類型的資源
@TransitionRes 表明該參數、變量或者函數返回值應該是一個 transition 類型的資源
@XmlRes 表明該參數、變量或者函數返回值應該是一個 XML 類型的資源
例如下面的函數在調用的時候,如果用非字符串類型的 R 常量則會給出警告:
import android.support.annotation.StringRes;
…
public abstract void setTitle(@StringRes int resId);
…
線程注解類型
線程注解用來檢測一個函數是否在指定類型的線程中執行。 有四個:@UiThread @MainThread @WorkerThread @BinderThread
注意: 其中 @UiThread 和 @MainThread 是可替換用的, 大部分應用中,這兩個是一樣的。
如果一個類中的所有函數都在同一個線程內執行,可以在 類名稱上面用這個注解即可。
權限注解類型
@RequiresPermission 用來表明該函數執行需要一個或者多個權限,如果你沒有聲明這些權限,則會給出警告。例如:
@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public abstract void setWallpaper(Bitmap bitmap) throws IOException;
@RequiresPermission(allOf = {
Manifest.permission.READ_HISTORY_BOOKMARKS,
Manifest.permission.WRITE_HISTORY_BOOKMARKS})
public static final void updateVisitedHistory(ContentResolver cr, String url, boolean real) {
…
}
@RequiresPermission(anyOf = {
Manifest.permission.READ_HISTORY_BOOKMARKS,
Manifest.permission.WRITE_HISTORY_BOOKMARKS})
public static final void updateHistory(ContentResolver cr, String url, boolean real) {
…
}
如果只要滿足多個權限中的一個,用 anyOf; 如果要滿足多個權限,用 allOf.
返回值是否使用檢測注解
@CheckResults 用來檢測函數的返回值是否被使用了,如果沒有使用則說明可能不應該調用這個函數,可以給出建議使用哪個函數。例如,新的 Android SDK 中就在 checkPermission 函數中使用如下注解:
@CheckResult(suggest=”#enforcePermission(String,int,int,String)”)
public abstract int checkPermission(@NonNull String permission, int pid, int uid);
如果你調用了 checkPermission 函數,但是并沒有使用其返回值,則很有可能你是想申請一個權限而不是檢查是否有這個權限,所以 suggest 參數建議你使用 enforcePermission 函數來申請權限。如果你確實想檢查是否有這個權限,則通常你會判斷 checkPermission 的返回值來確定是否有這個權限。
確保調用 super 函數的注解
@CallSuper 來表明重寫這個函數需要調用 super 父函數。如果你忘記了調用,則會提醒你。比如 Activity 的onCreate 函數需要代用 super.onCreate().
數值常量注解
@IntRange 是用來表明整數型參數的取值范圍的,比如 下面的 setAlpha 函數的參數 alpha 的取值范圍應該為 0 到 255,其他值都是非法的;
public void setAlpha(@IntRange(from=0,to=255) int alpha) { … }
@FloatRange 同樣是表明浮點數范圍的,例如:
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {…}
而 @Size 是用來表明數組類型參數的長度的,可以用 @Size(min=1) 來指定數組的最小長度,@Size(2) 則表明該數組參數必須是2. 例如:、
int[] location = new int[3];
button.getLocationOnScreen(@Size(min=1) location);
創建枚舉類型注解
如果一個參數、變量的取值是幾個常量中的一個,則可以用 @IntDef 和 @StringDef 注解來自定義一個常量枚舉類型注解。使用方式如下所示:
import android.support.annotation.IntDef;
…
public abstract class ActionBar {
…
//定義所接受的常量值
@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
//告訴編譯器該注解不會在 .class 文件中存在
@Retention(RetentionPolicy.SOURCE)
//定義 NavigationMode 注解
public @interface NavigationMode {}
//Declare the constants
public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;
//Decorate the target methods with the annotation
@NavigationMode
public abstract int getNavigationMode();
//Attach the annotation
public abstract void setNavigationMode(@NavigationMode int mode);
上面的 @NavigationMode 注解使用了 @IntDef 來定義該注解所限定了一些常量值。 當你用 @NavigationMode 注解時,則說明這個參數或者函數返回值需要是 @IntDef 中定義的常量值其一 (NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, 或者 NAVIGATION_MODE_TABS).
除了定義具體的常量值以外,還可以通過 flag 參數來指定一個模式,例如下面的 DisplayOptions 注解定義該類型必須滿足依 DISPLAY_ 開頭的一個模式。
import android.support.annotation.IntDef;
…
@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 {}
…