概要
Enum 是 java 中一种包含固定常数的类型,当我们需要预先定义一些值时,我们使用 Enum,这样做通常为了在编译时期避免接受额外常量引起的错误。
而且,Enum 增加了APK 的大小,比常量多5到10倍的内存占用,这是关于应用性能的最佳实践.
使用 Enum 的缺点
每一个枚举值都是一个对象,在使用它时会增加额外的内存消耗,所以枚举相比与 Integer 和 String 会占用更多的内存。
较多的使用 Enum 会增加 DEX 文件的大小,会造成运行时更多的开销,使我们的应用需要更多的空间。
如果你的应用使用很多的 Enum ,最好使用Integer 或 String 替代他们,但是这样还会有问题。
既然都说到这个份上了,那么有什么比较好的解决方法呢?
官方文档说明,安卓开发应避免使用Enum(枚举类),因为相比于静态常量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})
区别是第二种可以用条件进行位运算,更多详细信息,请参考官方文档
- 定义一个注解,表明当前@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})
总结
可以看到,如果不适用枚举,将会带来类型不安全的问题。一般情况下,我们在很多地方都会使用到枚举,因为方便和简洁。但是使用枚举也会产生占用内存过高等情况。所以我们可以有了自定义的方案,来限定我们使用的类型范围。