1. 程式人生 > >Android中註解(Support Annotations)的使用

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類