前言
Android Support Library 從 19.1 版本開始引入了一個(gè)新的注解庫陨舱,其中包含了很多的元注解搜贤,使用它們修飾我們的代碼, 可以讓我們提高程序的開發(fā)效率锯岖,讓我們更早的發(fā)現(xiàn)問題介袜。以及對(duì)代碼施以規(guī)范,讓代碼更加有可讀性出吹。這篇文章就來簡(jiǎn)單了解下這些注解遇伞,以及其使用。如有錯(cuò)誤和遺漏捶牢,歡迎留言和補(bǔ)充~
注:現(xiàn)在我們新建項(xiàng)目直接就依賴了 support.appcompat 包鸠珠,其中已經(jīng)依賴了 annotations 包加派。如果你的項(xiàng)目中寫如下注解報(bào)錯(cuò),可以添加注解包:
dependencies {
compile 'com.android.support:support-annotations:22.2.0'
}
@IntDef & @StringDef
替代 Java 中枚舉的注解跳芳,以 @IntDef 為例芍锦,定義和使用如下:
@IntDef({RED, BLUE, YELLOW})
@Retention(RetentionPolicy.SOURCE)
public @interface LightColors{};
public static final int RED = 1;
public static final int BLUE = 2;
public static final int YELLOW = 3;
public void setColor(@LightColors int color){
}
- @interface:聲明新的枚舉注解類型。
- @Retention(RetentionPolicy.SOURCE):告知編譯器不將枚舉的注解數(shù)據(jù)存儲(chǔ)在 .class 文件中飞盆。
如果允許常量與標(biāo)志(例如:|娄琉、& 和 ^ 等等)相結(jié)合,則我們可以使用 flag 屬性吓歇,如:
@IntDef(flag = true, value = {RED, BLUE, YELLOW})
使用:
setColor(RED | BLUE);
@Nullable & @NonNull
- @Nullable:注解的元素可以為 null孽水。
- @NonNull:注解的元素不可以為 null。
上面的注解可以修飾如下元素:
1城看,方法參數(shù)女气。如:
@Nullable
private String data;
2,方法的返回值测柠。 如:
@Nullable
public String getData(){
return data;
}
3炼鞠,成員屬性。如:
public void setData(@Nullable String data){
}
當(dāng)用空的參數(shù)傳給被 @NonNull 修飾的方法參數(shù)的方法時(shí)轰胁,會(huì)給出如下警告提示(編譯不會(huì)報(bào)錯(cuò)):
passing "null" argument to parameter annotated as @NotNull
@FloatRange & @IntRange
@FloatRange 和 @IntRange 是用于限定范圍的注解谒主。其中 @FloatRange 是限定 float 類型的,而 @IntRange 是限定 int 類型的赃阀。它們同上注解一樣霎肯,可以修飾方法參數(shù)、方法返回值榛斯、成員屬性观游。
以 @IntRange 為例,修飾方法參數(shù)的定義如下:
public void setAge(@IntRange(from = 1, to = 180) int age){
}
如果調(diào)用該方法傳的參數(shù)不在 1 - 180 的范圍內(nèi)驮俗, 如:setAge(0)懂缕,那么編譯會(huì)直接報(bào)如下錯(cuò):
value must be ≥ 1 and ≤ 180 (was 0)
@Size
@Size 注解的作用是限定長(zhǎng)度的,同上注解一樣意述,可以修飾方法參數(shù)提佣、方法返回值、成員屬性荤崇。
- 限定字符串的長(zhǎng)度:
public void setData(@Size(4) String data){
}
當(dāng)傳入的字符串長(zhǎng)度不等于 4 時(shí),編譯器會(huì)直接報(bào)錯(cuò):
Length must be exactly 4
- 限定數(shù)組的長(zhǎng)度:
public void setData(@Size(4) int[] data){
}
- 特殊的限定潮针,如限定為 2 的倍數(shù):
public void setData(@Size(multiple = 2) int[] data){
}
限定最小的長(zhǎng)度:
@Size(min = 2)
限定最大的長(zhǎng)度:
@Size(max = 2)
等同于 @Size(2)
寫法:
@Size(value = 2)
@RequiresPermission
該注解作用是表明方法所執(zhí)行的內(nèi)容需要權(quán)限术荤。如需要單個(gè)權(quán)限:
@RequiresPermission(Manifest.permission.CALL_PHONE)
private void callPhone(String phone){
}
需要一組權(quán)限:
@RequiresPermission(allOf = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE})
public static final void copyFile(String dest, String source) {
...
}
對(duì)于 intent 權(quán)限,我們可以定義在 intent 操作名稱的字符串上:
@RequiresPermission(android.Manifest.permission.BLUETOOTH)
public static final String ACTION_REQUEST_DISCOVERABLE =
"android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
對(duì)于需要單獨(dú)讀寫權(quán)限的內(nèi)容提供程序的權(quán)限每篷,我們可以在 @RequiresPermission.Read 或 @RequiresPermission.Write 注解中包含每個(gè)權(quán)限要求:
@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
如果權(quán)限依賴于提供給方法參數(shù)的特定值瓣戚,那么可以對(duì)參數(shù)本身使用 @RequiresPermission 而不用列出具體的權(quán)限端圈,如 startActivity(intent) 方法:
public abstract void startActivity(@RequiresPermission Intent intent, @Nullable Bundle) {...}
當(dāng)我們使用這種方式(間接權(quán)限)時(shí),構(gòu)建工具將執(zhí)行數(shù)據(jù)流分析以檢查傳遞到方法的參數(shù)是否具有任何 @RequiresPermission 注解子库。如:
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:1234567890"));
startActivity(intent);
這里的 startActivity(intent) 就直接報(bào)錯(cuò)了:
call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`
因?yàn)? Intent.ACTION_CALL 中標(biāo)記了權(quán)限注解:
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@RequiresPermission(Manifest.permission.CALL_PHONE)
public static final String ACTION_CALL = "android.intent.action.CALL";
@CheckResult
@CheckResult 注解是作用于方法上的舱权,作用是檢驗(yàn)有沒有處理返回值。如果沒有處理返回值則會(huì)報(bào)錯(cuò)仑嗅。
@CheckResult
public String getData(String data) {
return data.trim();
}
線程注解
線程注解可以檢查某個(gè)方法是否從特定類型的線程調(diào)用宴倍。支持以下線程注解:
- @MainThread:表示標(biāo)記的方法只應(yīng)在主線程調(diào)用。如果標(biāo)記的是一個(gè)類仓技,那么該類中的所有方法都應(yīng)該是在主線程被調(diào)用鸵贬。例:(通常,應(yīng)用程序的主線程也是 Ui 線程脖捻。但是阔逼,在特殊情況下,應(yīng)用程序的主線程可能不是其 Ui 線程)
@MainThread
public void deliverResult(D data) { ... }
- @UiThread:表示標(biāo)記的方法或構(gòu)造函數(shù)只應(yīng)該在 Ui 線程上調(diào)用地沮。如果標(biāo)記的是一個(gè)類嗜浮,那么該類中的所有方法都應(yīng)是在 Ui 線程被調(diào)用。例:
@UiThread
public abstract void setText(@NonNull String text) {...}
- @WorkerThread:表示標(biāo)記的方法只應(yīng)該在工作線程上調(diào)用摩疑。如果標(biāo)記的是一個(gè)類周伦,那么該類中的所有方法都應(yīng)是在一個(gè)工作線程上調(diào)用。例:
@WorkerThread
protected abstract FilterResults performFiltering(CharSequence constraint);
- @BinderThread:表示標(biāo)記的方法只應(yīng)在綁定線程上調(diào)用未荒。如果標(biāo)記的是一個(gè)類专挪,那么該類中的所有方法都應(yīng)是在綁定線程被調(diào)用。例:
@BinderThread
public BeamShareData createBeamShareData() { ... }
- @AnyThread:表示可以從任何線程調(diào)用帶標(biāo)記的方法片排。如果標(biāo)記的是一個(gè)類寨腔,那么該類中的所有方法都可以從任何線程中調(diào)用。例:
@AnyThread
public void deliverResult(D data) { ... }
構(gòu)建工具會(huì)將 @MainThread 和 @UiThread 注解視為可以互換率寡,因此迫卢,我們可以從 @MainThread 方法調(diào)用 @UiThread 方法,反之亦然冶共。不過如果系統(tǒng)應(yīng)用在不同線程上帶有多個(gè)試圖乾蛤,Ui 線程可與主線程不同。因此捅僵,我們應(yīng)該使用 @UiThread 標(biāo)注于應(yīng)用的視圖層次結(jié)構(gòu)關(guān)聯(lián)的方法家卖,使用 @MainThread 僅標(biāo)注于應(yīng)用生命周期關(guān)聯(lián)的方法。
資源注解
在 Android 中幾乎所有的資源都有其對(duì)于的 id庙楚,我們?cè)谑褂玫臅r(shí)候可以直接通過 id 來上荡,如:
textView.setText(getResources().getText(R.string.app_name));
但是這樣如果沒有寫指定的資源注解的話就會(huì)風(fēng)險(xiǎn),比如隨便傳了個(gè) 0馒闷,那么就會(huì)找不到對(duì)應(yīng)的資源酪捡。
為了避免由于自己的粗心大意而引發(fā)的錯(cuò)誤叁征,我們就可以使用資源注解了,如:
public int getText(@StringRes int id){
}
這樣當(dāng)我們調(diào)用該方法時(shí)逛薇,如果傳遞的參數(shù)并不是 String 類型的資源 id捺疼,那么編譯器就會(huì)報(bào)錯(cuò)提示。
除了 @StringRes
資源注解外永罚,還有:
- @IntegerRes:R.integer 類型資源啤呼。
- @AnimatorRes:R.animator 類型資源。
- @AnimRes:R.anim 類型資源尤蛮。
- @ArrayRes:R.array 類型資源媳友。
- @AttrRes:R.attr 類型資源。
- @BoolRes:R.bool 類型資源产捞。
- @ColorRes:R.color 類型資源醇锚。
- @DimenRes:R.dimen 類型資源。
- @DrawableRes:R.drawable 類型資源坯临。
- @FractionRes:R.fraction 類型資源焊唬。(百分比)
- @IdRes:R.id 類型資源。
- @InterpolatorRes:R.interpolator 類型資源看靠。(插值器)
- @LayoutRes:R.layout 類型資源赶促。
- @MenuRes:R.menu 類型資源。
- @PluralsRes:R.plurals 類型資源挟炬。(復(fù)數(shù))
- @RawRes:R.raw 類型資源鸥滨。
- @StyleableRes:R.styleable 類型資源。
- @StyleRes:R.style 類型資源谤祖。
- @TransitionRes: R.transition 類型資源婿滓。
- @XmlRes:R.xml 類型資源。
- @AnyRes:未知資源粥喜。(表示自己不知道是什么類型的資源凸主。比如有可能為 R.drawable 也有可能是 R.string。)
@ColorInt
@ColorInt 注解的作用為:限定顏色值额湘。(ARGB:0xAARRGGBB)
public void setColor(@ColorInt int color) {
}
如果直接使用資源 id卿吐,則會(huì)報(bào)錯(cuò),如下:
setColor(R.color.colorAccent)// 報(bào)錯(cuò)
正確的使用是:
setColor(0xFFFF00FF);
如果要使用資源 id锋华,則可以通過 ContextCompat.getColor() 方法來:
setColor(ContextCompat.getColor(context, R.color.colorAccent));
@CallSuper
該注解用于修飾方法嗡官,表示重寫該方法時(shí)必須調(diào)用 super 方法。如 onCreate() 方法:
@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}
重寫 onCreate() 方法時(shí)供置,必須調(diào)用 super 方法:
super.onCreate(savedInstanceState);
否則報(bào)錯(cuò)谨湘。
@VisibleForTesting & @Keep
使用 @VisibleForTesting 和 @Keep 注解可以表示方法、類芥丧、或字段的可訪問性紧阔。
@VisibleForTesting:該注解只起到一個(gè)注釋的作用,告訴其他開發(fā)者被標(biāo)記的代碼為什么有這么大的可見程度(為了測(cè)試方便)续担。因此擅耽,經(jīng)常用來修飾 public 或 protected,用來修飾 private 并不會(huì)報(bào)錯(cuò)物遇,但是沒有意義乖仇。
@Keep:標(biāo)記的指定代碼在混淆時(shí)不會(huì)被混淆。
參考: