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