如需转载请评论或简信,并注明出处,未经允许不得转载

目录

前言

Android官方training文档中有一句话

Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android

http://www.androiddocs.com/training/articles/memory.html

枚举类的好处

在java中,使用枚举类可以保证类型安全提高代码可读性。无论需求如何变化,比如枚举常量增加一个数据,或是增加一条状态,都可以很方便的实现,比直接在全局常量类里定义状态来的便捷

static与Enum比较

  • 第一步:有一个Android Project,编译后生成的dex的体积为3,465,228byte

  • 第二步:新增创建一个类,如下所示,编译后生成的dex的体积为3,465,532byte(比初始增加304byte

public class Test {    public static final int RED = 1;    public static final int BLACK = 2;    public static final int GREEN = 3;    public static final int YELLOW = 4;    public int fun(int color) {        switch (color) {            case RED:                return -1;            case BLACK:                return -2;            case GREEN:                return -3;            case YELLOW:                return -4;            default:                return 0;        }    }}
  • 第三步,改写第二步中创建的类,如下所示,编译后生成的dex的体积为3,466,568 byte(比初始增加1340byte,比使用static增加1036byte

初步结论:使用Enum会比static增加2-3倍的byte,从而增加apk的体积

Enum源码分析

我们创建了一个枚举类Color

public enum Color {    RED, GREEN, BLACK, YELLOW}

编译项目,在app/build/intermediates/javac/debug/classes/项目名称目录下,可以找到Color.class文件,执行javap -c Color.class命令对class文件进行反编译,结果如下所示

Compiled from "Color.java"public final class com.geekholt.kotlinandjavademo.Color extends java.lang.Enum {  public static final com.geekholt.kotlinandjavademo.Color RED;  public static final com.geekholt.kotlinandjavademo.Color GREEN;  public static final com.geekholt.kotlinandjavademo.Color BLACK;  public static final com.geekholt.kotlinandjavademo.Color YELLOW;  public static com.geekholt.kotlinandjavademo.Color[] values();    Code:       0: getstatic     #1                  // Field $VALUES:[Lcom/geekholt/kotlinandjavademo/Color;       3: invokevirtual #2                  // Method "[Lcom/geekholt/kotlinandjavademo/Color;".clone:()Ljava/lang/Object;       6: checkcast     #3                  // class "[Lcom/geekholt/kotlinandjavademo/Color;"       9: areturn  public static com.geekholt.kotlinandjavademo.Color valueOf(java.lang.String);    Code:       0: ldc           #4                  // class com/geekholt/kotlinandjavademo/Color       2: aload_0       3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;       6: checkcast     #4                  // class com/geekholt/kotlinandjavademo/Color       9: areturn  static {};    Code:       0: new           #4                  // class com/geekholt/kotlinandjavademo/Color       3: dup       4: ldc           #7                  // String RED       6: iconst_0       7: invokespecial #8                  // Method "":(Ljava/lang/String;I)V      10: putstatic     #9                  // Field RED:Lcom/geekholt/kotlinandjavademo/Color;      13: new           #4                  // class com/geekholt/kotlinandjavademo/Color      16: dup      17: ldc           #10                 // String GREEN      19: iconst_1      20: invokespecial #8                  // Method "":(Ljava/lang/String;I)V      23: putstatic     #11                 // Field GREEN:Lcom/geekholt/kotlinandjavademo/Color;      26: new           #4                  // class com/geekholt/kotlinandjavademo/Color      29: dup      30: ldc           #12                 // String BLACK      32: iconst_2      33: invokespecial #8                  // Method "":(Ljava/lang/String;I)V      36: putstatic     #13                 // Field BLACK:Lcom/geekholt/kotlinandjavademo/Color;      39: new           #4                  // class com/geekholt/kotlinandjavademo/Color      42: dup      43: ldc           #14                 // String YELLOW      45: iconst_3      46: invokespecial #8                  // Method "":(Ljava/lang/String;I)V      49: putstatic     #15                 // Field YELLOW:Lcom/geekholt/kotlinandjavademo/Color;      52: iconst_4      53: anewarray     #4                  // class com/geekholt/kotlinandjavademo/Color      56: dup      57: iconst_0      58: getstatic     #9                  // Field RED:Lcom/geekholt/kotlinandjavademo/Color;      61: aastore      62: dup      63: iconst_1      64: getstatic     #11                 // Field GREEN:Lcom/geekholt/kotlinandjavademo/Color;      67: aastore      68: dup      69: iconst_2      70: getstatic     #13                 // Field BLACK:Lcom/geekholt/kotlinandjavademo/Color;      73: aastore      74: dup      75: iconst_3      76: getstatic     #15                 // Field YELLOW:Lcom/geekholt/kotlinandjavademo/Color;      79: aastore      80: putstatic     #1                  // Field $VALUES:[Lcom/geekholt/kotlinandjavademo/Color;      83: return}

把上面的字节码文件翻译成java大致如下所示

public final class Color extends java.lang.Enum {    public static final Color RED;    public static final Color GREEN;    public static final Color BLACK;    public static final Color YELLOW;    static {        RED = new Color("RED", 0);        GREEN = new Color("GREEN", 1);        BLACK = new Color("BLACK", 2);        YELLOW = new Color("YELLOW", 3);        VALUES = new Color[]{RED, GREEN, BLACK, YELLOW};    }    public static Color[] values() {        Color tmp = new Color[VALUES.length];        system.arraycopy(VALUES, 0, tmp, 0, VALUES.length);        return tmp;    }    public static Color valueOf(String name) {        return Enum.valueOf(name);    }}

由此我们可以得出以下结论:

  • Enum中的每一个值都是一个Object,它的每个声明都会占用运行时的部分内存以便能够引用到这个Object。因此Enum的值会比对应的IntegerString所占用的内存多
  • 很多时候我们声明static变量只需要在已有的类中进行声明,而如果使用枚举类,就会多出一个类,最终则会增大Dex的体积,显然Enum的空间占用是远大于Integer常量或String常量的

解决方案

  1. 为了弥补 Android 平台不建议使用枚举的缺陷,官方推出了两个注解,@IntDef@StringDef,用来提供编译期的类型检查
  • 添加依赖
    在build.gradle文件中的依赖块中添加:
    dependencies { compile 'com.android.support:support-annotations:24.2.0' }

  • 声明常量和@IntDef

    @IntDef({      Color.RED,      Color.GREEN,      Color.BLACK,      Color.YELLOW})@Retention(RetentionPolicy.SOURCE)public @interface Color {   int RED = 1;   int GREEN = 2;   int BLACK = 3;   int YELLOW = 4;}

    这里@TypeDef注解使用了@interface来声明新的枚举注解类型。其中@IntDef@StringDef注解以及@Retention标注了新的注解,目的是定义这个枚举类型。而@Retentino(RententionPolicy.SOURCE)注解告诉编译器在生成.class文件时不保留枚举注解数据

  • 使用方法如下, 这样外界就无法传递 Color 之外的成员作为参数

    public static void doSth(@Color int color){   switch (color){      case Color.RED:          //do something          break;       case Color.GREEN:          break;       case Color.BLACK:          break;       case Color.YELLOW:          break;    }}
  1. 如果开启了Proguard可以在很多情况下将枚举Enum优化到整数对象。

结论

在android中使用枚举类不仅会增加apk体积,同时也会增加运行时内存,所以在架构设计上还是要慎用枚举类。如果希望进行编译期类型检查可以使用@IntDef@StringDef类保证类型安全

更多相关文章

  1. Android开发指南中文版(七)Content Providers
  2. Android的路由框架用法
  3. Android注解式绑定控件,没你想象的那么难
  4. Android edittext 输入框 下一项 android:imeOptions="actionNex
  5. 如何检测android上的多媒体文件属于音频、视频还是图片?
  6. APK瘦身神器:Android App Bundles-让应用体积减少25%
  7. android 4.2.1 一种高效log打开方式
  8. 【android】两类不能在SDK中使用的API------@hide和internal API
  9. Android中指定Action、Category调用系统Activity

随机推荐

  1. android 网络访问-图片处理优秀开源项目
  2. android获取GPS位置信息
  3. BaseAdapter与ListView解析
  4. 2018-03-10—Android六大布局之LinearLay
  5. PreferenceActivity和PreferenceFragment
  6. Android 4游戏编程入门经典
  7. Android: Android Bluetooth
  8. Android根据文件路径使用File类获取文件
  9. Android Fresco图片处理库用法API英文原
  10. Android外设存储设备的访问及测试