Android開發(fā)@IntDef完美替代Enum (枚舉)

概要

Enum 是 java 中一種包含固定常數的類型,當我們需要預先定義一些值時,我們使用 Enum,這樣做通常為了在編譯時期避免接受額外常量引起的錯誤。

而且,Enum 增加了APK 的大小,比常量多5到10倍的內存占用,這是關于應用性能的最佳實踐.

使用 Enum 的缺點

每一個枚舉值都是一個對象,在使用它時會增加額外的內存消耗,所以枚舉相比與 Integer 和 String 會占用更多的內存。

較多的使用 Enum 會增加 DEX 文件的大小,會造成運行時更多的開銷,使我們的應用需要更多的空間。

如果你的應用使用很多的 Enum ,最好使用Integer 或 String 替代他們,但是這樣還會有問題。

既然都說到這個份上了,那么有什么比較好的解決方法呢?

官方文檔說明,安卓開發(fā)應避免使用Enum(枚舉類),因為相比于靜態(tài)常量Enum會花費兩倍以上的內存。參考這里
那么如果需要使用Enum應該怎么做呢?

  • 解決方案

既然是因為參數的類型太泛了造成的類型不安全,那么我只要將參數限定在某一個類型集合里面,不就大功告成了?!

是滴,一下就是要將的@IntDef/@StringDef + @interface來進行限定參數。


使用注解庫

這些注解不是默認加載的,它們被包裝為一個單獨的庫。Support Library現在是由一些更小的庫組成的,包括:v4-support、appcompat、gridlayout、mediarouter等等。
添加注解的最簡單的方法就是打開Project Structure對話框。首先在左邊選中module,然后右邊選中Dependencies標簽,點擊“+”號按鈕,選擇Library Dependency。如果SDK中已經包括了Android Support庫,那么注解支持庫就會顯示在快捷選擇列表中了,只需要點擊選擇就可以。

  • 步驟1:點擊Project Structure按鈕


    這里寫圖片描述
  • 步驟2:選中Dependencies標簽,點擊“+”號按鈕


    這里寫圖片描述
  • 步驟3:在下拉列表中選中support-annotations庫


    這里寫圖片描述
  • 點擊OK確定,這將會修改build.gradle文件。當然也可以手動在Gradle中添加如下依賴:

dependencies {  
    compile 'com.android.support:support-annotations:23.1.0'  
} 

應用

  • 定義static final的常量
private static final int ADD = 0;
private static final int SUB = 1;
private static final int MUL = 2;
private static final int DIV = 3;
  • 定義一個IntDef注解,包含上面的常量,兩種形式
@IntDef({ADD,SUB,MUL,DIV})

@IntDef(flag = true, value = {ADD,SUB,MUL,DIV})

區(qū)別是第二種可以用條件進行位運算,更多詳細信息,請參考官方文檔

  • 定義一個注解,表明當前@IntDef的保留策略,只保留源碼中,編譯時刪除,
@Retention(RetentionPolicy.SOURCE)

當然你還可以指定其他策略:

Class:編譯時被保留,在class文件中存在,但JVM將會忽略

Runtime:將被JVM保留,所以他們能在運行時被JVM或其他使用反射機制的代碼所讀取和使用

  • 自定義一個注解 表明類型
public @interface Operation{}
  • 使用,在方法中使用,類型安全,替代枚舉
public void operation(@Operation int opeartion) {
    switch (opeartion) {
      case ADD:
        break;
      case SUB:
        break;
      case DIV:
        break;
      case MUL:
        break;
    }
  }


Android 中有使用到的一個例子

Toast

public class Toast {
    static final String TAG = "Toast";
    static final boolean localLOGV = false;

    /** @hide */
    /*定義部分*/
    @IntDef({LENGTH_SHORT, LENGTH_LONG})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Duration {}

    public static final int LENGTH_SHORT = 0;
    public static final int LENGTH_LONG = 1;

    ...

    /*作為類型使用時*/
     /**
     * Set how long to show the view for.
     * @see #LENGTH_SHORT
     * @see #LENGTH_LONG
     */
    public void setDuration(@Duration int duration) {
        mDuration = duration;
    }

    /*做為返回值時*/
    /**
     * Return the duration.
     * @see #setDuration
     */
    @Duration
    public int getDuration() {
        return mDuration;
    }

}
  • ps :這里是IntDef的API說明
/*IntDef
implements Annotation
android.support.annotation.IntDef
Class Overview
Denotes that the annotated element of integer type, represents a logical type and that its value should be one of the explicitly named constants. If the IntDef#flag() attribute is set to true, multiple constants can be combined.
*/

//Example:

@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();

//For a flag, set the flag attribute:
@IntDef(
      flag = true
      value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})

總結

可以看到,如果不適用枚舉,將會帶來類型不安全的問題。一般情況下,我們在很多地方都會使用到枚舉,因為方便和簡潔。但是使用枚舉也會產生占用內存過高等情況。所以我們可以有了自定義的方案,來限定我們使用的類型范圍。

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容