android 枚举对象 默认值,Android中避免使用枚举类(Enum)

本文探讨了Android中使用枚举类(Enum)与静态常量在内存占用上的差异,发现Enum会显著增加APK体积。通过源码分析,解析了Enum内部机制。为解决这一问题,Android提供了@IntDef和@StringDef注解进行编译期类型检查。最后,建议在架构设计时谨慎使用Enum,考虑使用编译期检查替代。

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

05ab8bd3c713

目录

05ab8bd3c713

前言

Android官方training文档中有一句话

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

05ab8bd3c713

枚举类的好处

在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的值会比对应的Integer和String所占用的内存多

很多时候我们声明static变量只需要在已有的类中进行声明,而如果使用枚举类,就会多出一个类,最终则会增大Dex的体积,显然Enum的空间占用是远大于Integer常量或String常量的

解决方案

为了弥补 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;

}

}

如果开启了Proguard可以在很多情况下将枚举Enum优化到整数对象。

结论

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值