Android中註解(Support Annotations)的使用
簡介
註解(Annotation),也叫元資料。一種程式碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、介面、列舉是在同一個層次。它可以宣告在包、類、欄位、方法、區域性變數、方法引數等的前面,用來對這些元素進行說明。善用註解可提高您的程式碼質量和效率。
JDK自帶的註解
@Override-----------------------表示當前方法覆蓋了父類的方法
@Deprecated-------------------表示方法已經過時,方法上有橫線,使用時會有警告。
@SuppressWarnings-------表示關閉一些警告資訊(通知java編譯器忽略特定的編譯警告)
下面我們進行一個示例來演示這3種註解的使用
定義一個Animal的抽象類,包括3個方法,其中eat()方法設定為過時的,方法頭加上@Deprecated:
public abstract class Animal { privatestatic final String TAG = Animal.class.getSimpleName(); /** * eat *@deprecated Use {@link #feed()} */ @Deprecated public voideat() { feed(); } /** * feed */ public voidfeed() { Log.e(TAG, "feed"); } /** * sleep */ public voidsleep() { Log.e(TAG, "sleep"); } }
新建一個Pig的類,使繼承於Animal,因為它重寫了sleep()方法,所以在其方法上加上@Override:
public class Pig extends Animal {
privatestatic final String TAG = Pig.class.getSimpleName();
@Override
public voidsleep() {
Log.e(TAG, "sleep");
}
}
假設,我們在一個叫test()的方法中例項化一個Pig物件,然後分別呼叫它的eat()、feed()和sleep()方法:
private void test() { Animal pig= new Pig(); pig.eat(); pig.feed(); pig.sleep(); }
可以在你的IDE中看到呼叫pig.eat();的程式碼是被加上了劃線的,這是一個程式碼的警告,因為我們之前把eat()方法設定為過時的,若此時想不顯示此警告,可以在test()方法上加入@SuppressWarnings("deprecation"),如:
@SuppressWarnings("deprecation")
private void test() {
Animal pig= new Pig();
pig.eat();
pig.feed();
pig.sleep();
}
這裡說明一下,@SuppressWarnings註解接收一個字串引數,其引數含義是:
@SuppressWarnings("unchecked")--------------未檢查的轉化,如集合沒有指定型別
@SuppressWarnings("unused")--------------------未使用的變數
@SuppressWarnings("resource")------------------有泛型未指定型別
@SuppressWarnings("path")-------------------------在類路徑,原檔案路徑中有不存在的路徑
@SuppressWarnings("deprecation")------------使用了某些不贊成使用的類和方法
@SuppressWarnings("fallthrough")--------------switch語句執行到底沒有break關鍵字
@SuppressWarnings("serial")------------------------某類實現Serializable 但是沒有定義serialVersionUID這個需要但是不必須的欄位
@SuppressWarnings("rawtypes")-----------------沒有傳遞帶有泛型的引數
@SuppressWarnings("all") ----------------------------全部型別的警告
Android中的註解
除了Java JDK自帶的3種註解外,Android中也有相應型別的註解,分別是Nullness註解、資源註解、執行緒註解、值約束註解、許可權註解、返回值註解、CallSuper註解、Typedef 註解、程式碼可訪問性註解,等。下面看看這些註解的是如何使用的
Nullness註解
Nullness註解有:@Nullable 和@NonNull。它們是用於檢查給定變數、引數或返回值是否為null的情況。@Nullable指示可以為null,而@NonNull則指示不可為null。
示例:
public class Person {
@NonNull
privateString mName;
@Nullable
publicString getName() {
returnmName;
}
public voidsetName(@NonNull String name) {
mName =name;
}
}
在Person類中全域性變數mName和方法setName()的引數都標註為不可為空,而getName()方法標註為返回引數可為空,接著再來看看錯誤的呼叫程式碼:
@NonNull
private String mMyName;
……
Person person = new Person();
// 下面三行程式碼是一個錯誤的演示,都會被IDE給出相應的警告
mMyName = null;
person.setName(null);
mMyName = person.getName();
資源註解
資源註解有:@StringRes、@DrawableRes、@DimenRes、@ColorRes、@InterpolatorRes、@LayoutRes。等。因為 Android 對資源的引用以整型形式傳遞,所以它們的作用是用於檢查引數資源型別是否合法。其中,如果您的引數支援多種資源型別,您可以在給定引數上新增多個註解。使用@AnyRes能夠指示註解的引數可為任意型別的R資源。
示例:
void setColor(@ColorRes int color) {
// ...
}
呼叫:
setColor(R.string.app_name); // 錯誤的呼叫
setColor(R.color.colorWhite); // 正確的呼叫
在使用@ColorRes指定引數應為顏色資源後,若顏色整型(RRGGBB 或 AARRGGBB 格式)仍然是無法識別為顏色資源。這種情況,請改用 @ColorInt 註解指示引數必須為顏色整型。
示例:
void setColor2(@ColorInt int color) {
// ...
}
呼叫:
setColor2(R.color.colorWhite); // 錯誤的呼叫
setColor2(0x80F6F6F6); // 正確的呼叫
執行緒註解
執行緒註解有:@MainThread、@UiThread、@WorkerThread、@BinderThread和@AnyThread。它們用於檢查某個方法是否從特定型別的執行緒呼叫。其中,構建工具會將 @MainThread 和 @UiThread 註解視為可互換,因此,您可以從 @MainThread 方法呼叫 @UiThread 方法,反之亦然。
值約束註解
值約束註解有:@IntRange、@FloatRange 和@Size 。它們用於驗證傳遞的引數的值的取值範圍或長度。
示例:
public void setAlpha(@IntRange(from=0, to=255) intalpha) {
// ...
}
public void setAlpha(@FloatRange(from=0.0, to=1.0)float alpha) {
// ...
}
public void setLocation(@Size(min=1) int[]location) {
// ...
}
public void setLocation2(@Size(max=2) int[]location){
// ...
}
public void setLocation3(@Size(3) int[]location) {
// ...
}
這裡說下@Size註解,它是可以檢查集合或陣列的大小,以及字串的長度,上面示例中,setLocationset的傳入引數指定陣列要求最小大小為1;而setLocationset2則指定要求最大大小為2;最後setLocation3是指定要求確切大小為3。
許可權註解
使用@RequiresPermission註解可以驗證方法呼叫方的許可權。要檢查有效許可權列表中是否存在某個許可權,請使用anyOf 屬性。要檢查是否存在一組許可權,請使用allOf 屬性。
示例1,若要呼叫setWallpaper()方法,要確保方法的呼叫方擁有permission.SET_WALLPAPERS許可權:
@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public abstract void setWallpaper(Bitmap bitmap)throws IOException;
示例2,若要呼叫copyFile()方法,要確保呼叫方同時具有外部儲存空間的讀寫許可權:
@RequiresPermission(allOf = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE})
public static final void copyFile(String dest, Stringsource) {
...
}
示例3,intent許可權的檢查:
public void startMyActivity(@RequiresPermissionIntent intent) {
startActivity(intent);
}
呼叫:
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:123456789"));
startMyActivity(intent);
這時,若呼叫方沒有打電話許可權,則IDE會有警告:Missing permissions required by intentIntent.ACTION_CALL:android.permsiion.CALL_PHONE
示例4,若需要單獨讀寫許可權的內容提供程式的許可權,可使用:@RequiresPermission.Read或@RequiresPermission.Write註解指寫許可權要求:
@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI =Uri.parse("content://browser/bookmarks");
返回值註解
返回值註解:@CheckResult可以驗證實際使用的是方法的結果還是返回值。該註解意味著需要呼叫方對方法的返回值進行處理
示例:
@CheckResult
public boolean checkValid(String value) {
returnTextUtils.isEmpty(value);
}
呼叫:
checkValid(""); // 錯誤的呼叫,因為呼叫方法後返回的值沒有對其作處理
boolean check = checkValid(""); // 正確的呼叫
CallSuper註解
@CallSuper註解可以驗證子類中重寫父類的方法一定需要同時呼叫父類實現,即要求子類呼叫super.XX()。
示例:
// 父類程式碼:
public abstract class Animal {
@CallSuper
public voidsleep() {
}
}
// 子類程式碼,若不呼叫super.sleep();,則IDE會給出警告:Overriding method should callsuper.sleep
public class Pig extends Animal {
@Override
public voidsleep() {
// super.sleep();
}
}
Typedef 註解
Typedef註解,有些人會叫它列舉註解,因為它的作用跟列舉類似。Typedef 註解可以確保特定引數、返回值或欄位引用特定的常量集。它們還可以完成程式碼以自動提供允許的常量。
Typedef 註解使用@interface宣告新的列舉註解型別。@IntDef或@StringDef註解以及@Retention可以標註新註解,並且為定義列舉的型別所必需。@Retention(RetentionPolicy.SOURCE) 註解可以告知編譯器不將列舉的註解資料儲存在.class檔案中。
示例1:
public static final int LENGTH_LONG = 1;
public static final int LENGTH_SHORT = 0;
@IntDef({LENGTH_SHORT, LENGTH_LONG})
@Retention(RetentionPolicy.SOURCE)
public @interface Duration {}
public void setDuration(@Duration int duration) {
//mDuration = duration;
}
@Duration
public int getDuration() {
returnLENGTH_LONG;
}
上面示例,其實是Toast的部分原始碼,我們在設定Toast顯示時間時,只能設定兩個值:LENGTH_LONG 和 LENGTH_SHORT就是這樣做到的
示例2:
@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 {
// ...
}
@IntDef註解中,還可以指定flag的值(若不指定則為false),如果我們將flag設為true,則表示可以將允許常量與標誌(例如,|、& 和 ^,等等)相結合
程式碼可訪問性註解
@Keep註解用於標註類或方法在混淆的時候將不會被混淆
其他常見的註解
@TargetApi註解用於遮蔽IDE提示要求某一新API版本的錯誤
示例,一段程式碼要求Android5.0才行效,但我們又明確知道非Android5.0的情況不會呼叫到該段程式碼:
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void test() {
// ...
}
@SuppressLint註解跟@TargetApi註解類似,不同的是@SuppressLint註解用於遮蔽IDE提示要求的一切API版本的錯誤,注意是一切
@Widget註解用於表示該類是自定義的Widget類