安卓註解使用介紹

429路發表於2019-03-14

在Java中,註解(Annotation)引入始於Java5,用來描述Java程式碼的元資訊,通常情況下註解不會直接影響程式碼的執行,儘管有些註解可以用來做到影響程式碼執行。

在程式碼檔案中使用‘@’字元告訴編譯器接下來的是一個註解。註解可以用在類,構造方法,成員變數,方法,引數等的宣告中。作用主要是對編譯器警告等輔助工具產生影響,如果傳遞了錯誤的型別那麼編譯器就會發出警告,這樣就可以在編碼和維護的過程中輔助發現問題,提高開發效率,提升程式碼質量,也促進形成編碼規範。

安卓開發中用到的註解主要有四個方面:JDK內建註解、JDK自定義註解包、android sdk內建註解、android.support.annotation註解。

安卓註解使用介紹

JDK內建註解

如下圖,左側是JDK提供的三個標準註解,在java.lang包內。右側JDK提供的四個是對自定義註解的支援,在java.lang.annotation包內。

安卓註解使用介紹

@Deprecated是一個標記註解,表示被標記的成員變數或者成員方法已經不建議使用,原因可能是這個方法/變數有缺陷或者在新的SDK中已經不被支援。

@Override註解在繼承過程中,標識對父類方法的覆蓋關係。這個在Java中不是必須的,但是建議在需要的地方強制使用。防止在子類或者父類中誤操作修改方法簽名或者遺漏相關的程式碼(kotlin中對於子類覆蓋父類方法強制使用override關鍵字,不再需要註解標記)。

@SuppressWarnings用來抑制編譯器生成警告資訊,對指定型別的警告保持靜默。可以修飾類、方法、方法引數、屬性和區域性變數,採用就近原則,儘量放在被需要靜默的警告語句附近。接收一個字串或者一個字符集作為引數(引數詳細介紹參考文章),它指示將取消的警告。

JDK提供了四種元註解,支援使用者對註解進行自定義擴充套件。自定義註解後面會詳細介紹,這裡先略過。

Android SDK內建註解

Android SDK註解有兩個@SuppressLint和@TargetApi。

安卓註解使用介紹
這兩個註解是使用Lint靜態檢測對應的標記。如果禁用了Lint,用或者不用這些註解都沒有太大關係。

@TargetApi:Android工程需要設定所支援的最小的系統版本,Android Studio是在Gradle中設定minSdkVersion的值。如果某個方法或者類被宣告需要在某個版本和更高版本的系統上執行,可以使用@RequiresApi(requires)宣告支援的最小系統版本。當在宣告minSdkVersion的工程中使用了requires大於minSdkVersion的類或者方法時,Lint就會報錯誤提醒,這時候可以使用@TargetApi使Lint保持靜默,但是要新增程式碼為低版本的系統提供對應的備選方案,否則在低版本系統上執行會產生崩潰。

@SuppressLint:上面的@TargetApi註解只針對API版本進行註解,使Lint對版本錯誤保持靜默。@SuppressLint針對的範圍更廣,通過設定一個引數(identified by the lint issue id)通知Lint對相應的警告⚠和錯誤❎️保持靜默。

@SuppressLint("NewApi"),這個註解可以實現@TargetApi(version)相同的作用,只是沒有指定特定的API版本,導致工程師和Lint都不知道響應的範圍,容易導致錯誤,不建議使用。

android support註解

Android Support Library提供com.android.support:support-annotations對Android的註解進行了擴充。下圖以v25為例列出了所有定義的46個註解(到v27增加了5個,分別為ColorLong、FontRes、GuardedBy、HalfFloat、NavigationRes)。

安卓註解使用介紹

  • 其中22個資源類註解,如下:
    安卓註解使用介紹
  • 取值範圍類3個註解,IntRange/FloatRange對響應型別的變數或引數規定取值範圍,引數有from和to兩個;Size對陣列的長度約束,引數min/max(含)組合宣告陣列的長度範圍,引數value指定資料的具體長度,引數multiple指定陣列長度必須是某個數字的倍數。
  • Android中新引入的替代列舉的註解有IntDef和StringDef,他們唯一的區別一個是int型別,一個是string型別,下面我們列一下官方API文件中給出的使用方法。

列舉型別的註解的使用,先定義一系列可用的取值,然後定義一個註解使用@IntDef或者@StringDef指定新的註解可用的取值列表。

使用 Enum 的缺點:每一個列舉值都是一個物件,在使用它時會增加額外的記憶體消耗,所以列舉相比於Integer和String會佔用更多的記憶體,較多的使用 Enum 會增加 DEX 檔案的大小,會造成執行時更多的開銷,使我們的應用需要更多的空間。特別是分dex的大APP,列舉的初始化很容易導致ANR。

@Retention(SOURCE)
@StringDef({
    POWER_SERVICE,
    WINDOW_SERVICE,
    LAYOUT_INFLATER_SERVICE
})
public @interface ServiceName {}
public static final String POWER_SERVICE = "power";
public static final String WINDOW_SERVICE = "window";
public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
複製程式碼
@Retention(SOURCE)
@IntDef({
    NAVIGATION_MODE_STANDARD, 
    NAVIGATION_MODE_LIST, 
    NAVIGATION_MODE_TABS
})
public @interface NavigationMode {}
public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;
  ...
public abstract void setNavigationMode(@NavigationMode int mode);
@NavigationMode
public abstract int getNavigationMode();
複製程式碼

自定義註解

上面的列舉註解,就是自定義註解的例子。自定義註解使用關鍵字@interface宣告,然後用相應的屬性修飾。

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Entity {
    String value();
    String name();
}
複製程式碼
  • @Documented表示擁有該註解的元素可通過javadoc此類的工具進行文件化。該型別應用於註解那些影響客戶使用帶註釋(comment)的元素宣告的型別。如果型別宣告是用Documented來註解的,這種型別的註解被作為被標註的程式成員的公共API。
  • @Inherited:表示該註解型別被自動繼承
  • @Retention:表示該註解型別的註解保留的時長。可用的引數都在列舉型別RetentionPolicy中給出了定義。當註解型別宣告中沒有@Retention元註解,則預設保留策略為RetentionPolicy.CLASS。
  • @Target:表示該註解型別的所使用的程式元素型別。可用的引數都在列舉型別ElementType中給出了定義。當註解型別宣告中沒有@Target元註解,則預設為可適用所有的程式元素。

附錄:ButterKnife

安卓註解使用介紹
使用ButterKnife只需要在module的gradle檔案中加入下面程式碼:

implementation 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
複製程式碼

在library工程中,直接使用R.xxx.xxx會報“元素值必須為常量表示式”的錯誤提示,處理步驟如下:

  • 在根gradle檔案中新增(版本根據需要變化):
classpath 'com.jakewharton:butterknife-gradle-plugin:8.4.0'
複製程式碼
  • 在module的gradle中新增:
apply plugin: 'com.jakewharton.butterknife'
複製程式碼
  • R.xxx.xxx替換成R2.xxx.xxx

參考文章

相關文章