Android Support Library(安卓註解庫) 從 19.1 版本開始引入了一個新的註解庫,其中包含了很多的元註解,使用它們修飾我們的代碼, 可以讓我們提高程序的開發效率,讓我們更早的發現問題。以及對代碼施以規範,讓代碼更加有可讀性。這篇文章就來簡單瞭解下這些註解,以及其使用。如有錯誤和遺漏,歡迎留言和補充~
注:現在我們新建項目直接就依賴了 support.appcompat 包,其中已經依賴了 annotations 包。如果你的項目中寫如下註解報錯,可以添加註解包:
dependencies{
compile'com.android.support:support-annotations:22.2.0'
}
@IntDef & @StringDef
替代 Java 中枚舉的註解,以 @IntDef 爲例,定義和使用如下:
@IntDef({RED, BLUE, YELLOW})
@Retention(RetentionPolicy.SOURCE)
public@interfaceLightColors{};
publicstaticfinalintRED = 1;
publicstaticfinalintBLUE = 2;
publicstaticfinalintYELLOW = 3;
publicvoidsetColor(@LightColors intcolor){
}
- @interface:聲明新的枚舉註解類型。
- @Retention(RetentionPolicy.SOURCE):告知編譯器不將枚舉的註解數據存儲在 .class 文件中。
如果允許常量與標誌(例如:|、& 和 ^ 等等)相結合,則我們可以使用 flag 屬性,如:
@ IntDef( flag= true, value = { RED, BLUE, YELLOW})
使用:
setColor(RED | BLUE);
@Nullable & @NonNull
@Nullable:註解的元素可以爲 null。
@NonNull:註解的元素不可以爲 null。
上面的註解可以修飾如下元素:
- 1,方法參數。如:
@Nullable
private String data;
- 2,方法的返回值。 如:
@Nullable
public String getData(){
return data;
}
- 3,成員屬性。如:
public void setData(@Nullable String data){
}
當用空的參數傳給被 @NonNull 修飾的方法參數的方法時,會給出如下警告提示(編譯不會報錯):
passing "null"argument to parameter annotated as@NotNull
@FloatRange & @IntRange
@FloatRange 和 @IntRange 是用於限定範圍的註解。其中 @FloatRange 是限定 float 類型的,而 @IntRange 是限定 int 類型的。它們同上註解一樣,可以修飾方法參數、方法返回值、成員屬性。
以 @IntRange 爲例,修飾方法參數的定義如下:
publicvoidsetAge(@IntRange(from= 1, to = 180) intage){
}
如果調用該方法傳的參數不在 1 - 180 的範圍內, 如:setAge(0),那麼編譯會直接報如下錯:
value must be ≥ 1and≤ 180(was 0)
@Size
@Size 註解的作用是限定長度的,同上註解一樣,可以修飾方法參數、方法返回值、成員屬性。
限定字符串的長度:
publicvoid setData( @Size(4)String data){
}
當傳入的字符串長度不等於 4 時,編譯器會直接報錯:
Lengthmust be exactly 4
限定數組的長度:
publicvoidsetData(@Size(4) int[] data){
}
特殊的限定,如限定爲 2 的倍數:
publicvoidsetData(@Size(multiple = 2) int[] data){
}
限定最小的長度:
@ Size( min= 2)
限定最大的長度:
@ Size( max= 2)
等同於 @Size(2) 寫法:
@ Size( value= 2)
@RequiresPermission
該註解作用是表明方法所執行的內容需要權限。如需要單個權限:
@ RequiresPermission( Manifest. permission. CALL_PHONE)
private void callPhone(String phone){
}
需要一組權限:
@RequiresPermission(allOf = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE})
publicstaticfinalvoidcopyFile(String dest, String source){
...
}
對於 intent 權限,我們可以定義在 intent 操作名稱的字符串上:
@ RequiresPermission( android. Manifest. permission. BLUETOOTH)
public static final String ACTION_REQUEST_DISCOVERABLE =
"android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
對於需要單獨讀寫權限的內容提供程序的權限,我們可以在 @RequiresPermission.Read或 @RequiresPermission.Write註解中包含每個權限要求:
@RequiresPermission.Read( @RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write( @RequiresPermission(WRITE_HISTORY_BOOKMARKS))
publicstatic finalUri BOOKMARKS_URI = Uri.parse( "content://browser/bookmarks");
如果權限依賴於提供給方法參數的特定值,那麼可以對參數本身使用 @RequiresPermission 而不用列出具體的權限,如 startActivity(intent) 方法:
publicabstractvoidstartActivity(@RequiresPermission Intent intent, @Nullable Bundle) {...}
當我們使用這種方式(間接權限)時,構建工具將執行數據流分析以檢查傳遞到方法的參數是否具有任何 @RequiresPermission 註解。如:
Intent intent = newIntent(Intent.ACTION_CALL);
intent.setData(Uri.parse( "tel:1234567890"));
startActivity(intent);
這裏的 startActivity(intent) 就直接報錯了:
callrequires permission which may be rejected byuser: code should explicitly checktosee ifpermission isavailable ( with
`checkPermission`) orexplicitly handle a potential `SecurityException`
因爲 Intent.ACTION_CALL 中標記了權限註解:
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@RequiresPermission(Manifest.permission.CALL_PHONE)
publicstatic finalString ACTION_CALL = "android.intent.action.CALL";
@CheckResult
@CheckResult 註解是作用於方法上的,作用是檢驗有沒有處理返回值。如果沒有處理返回值則會報錯。
@CheckResult
publicString getData(String data) {
returndata.trim();
}
線程註解
線程註解可以檢查某個方法是否從特定類型的線程調用。支持以下線程註解:
@MainThread:表示標記的方法只應在主線程調用。如果標記的是一個類,那麼該類中的所有方法都應該是在主線程被調用。例:(通常,應用程序的主線程也是 Ui 線程。但是,在特殊情況下,應用程序的主線程可能不是其 Ui 線程)@MainThread
publicvoiddeliverResult(D data){ ... }
@UiThread:表示標記的方法或構造函數只應該在 Ui 線程上調用。如果標記的是一個類,那麼該類中的所有方法都應是在 Ui 線程被調用。例:@UiThread
publicabstractvoidsetText(@NonNull String text){...}
@WorkerThread:表示標記的方法只應該在工作線程上調用。如果標記的是一個類,那麼該類中的所有方法都應是在一個工作線程上調用。例:@WorkerThread
protectedabstractFilterResults performFiltering(CharSequence constraint);
@BinderThread:表示標記的方法只應在綁定線程上調用。如果標記的是一個類,那麼該類中的所有方法都應是在綁定線程被調用。例:@BinderThread
publicBeamShareData createBeamShareData(){ ... }
@AnyThread:表示可以從任何線程調用帶標記的方法。如果標記的是一個類,那麼該類中的所有方法都可以從任何線程中調用。例:@AnyThread
publicvoiddeliverResult(D data){ ... }
構建工具會將 @MainThread 和 @UiThread 註解視爲可以互換,因此,我們可以從 @MainThread 方法調用 @UiThread 方法,反之亦然。不過如果系統應用在不同線程上帶有多個試圖,Ui 線程可與主線程不同。因此,我們應該使用 @UiThread 標註於應用的視圖層次結構關聯的方法,使用 @MainThread 僅標註於應用生命週期關聯的方法。
資源註解
在 Android 中幾乎所有的資源都有其對於的 id,我們在使用的時候可以直接通過 id 來,如:
textView.setText( getResources() .getText( R.string.app_name));
但是這樣如果沒有寫指定的資源註解的話就會風險,比如隨便傳了個 0,那麼就會找不到對應的資源。
爲了避免由於自己的粗心大意而引發的錯誤,我們就可以使用資源註解了,如:
publicintgetText(@StringRes intid){
}
這樣當我們調用該方法時,如果傳遞的參數並不是 String 類型的資源 id,那麼編譯器就會報錯提示。
除了 @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 類型資源。(複數)
@RawRes:R.raw 類型資源。
@StyleableRes:R.styleable 類型資源。
@StyleRes:R.style 類型資源。
@TransitionRes: R.transition 類型資源。
@XmlRes:R.xml 類型資源。
@AnyRes:未知資源。(表示自己不知道是什麼類型的資源。比如有可能爲 R.drawable 也有可能是 R.string。)
@ColorInt
@ColorInt 註解的作用爲:限定顏色值。(ARGB:0xAARRGGBB)
publicvoidsetColor(@ColorInt intcolor){
}
如果直接使用資源 id,則會報錯,如下:
setColor(R.color.colorAccent) // 報錯
正確的使用是:
setColor(0xFFFF00FF);
如果要使用資源 id,則可以通過 ContextCompat.getColor() 方法來:
setColor( ContextCompat.getColor( context, R.color.colorAccent));
@CallSuper
該註解用於修飾方法,表示重寫該方法時必須調用 super 方法。如 onCreate() 方法:
@CallSuper
protectedvoidonCreate(Bundle savedInstanceState){
}
重寫 onCreate() 方法時,必須調用 super 方法:
super.onCreate(savedInstanceState);
否則報錯。
@VisibleForTesting & @Keep
使用 @VisibleForTesting 和 @Keep 註解可以表示方法、類、或字段的可訪問性。
@VisibleForTesting:該註解只起到一個註釋的作用,告訴其他開發者被標記的代碼爲什麼有這麼大的可見程度(爲了測試方便)。因此,經常用來修飾 public 或 protected,用來修飾 private 並不會報錯,但是沒有意義。@Keep:標記的指定代碼在混淆時不會被混淆。