在项目开发中,在需要定义多个常量的开发场景(如上传文件逻辑的状态回调),会犹豫是应该使用Enum枚举还是直接定义Constants常量来解决问题。
查看 Google Developer Guide, 有提到
Avoid enumerations
A single enum can add about 1.0 to 1.4 KB of size to your app'sclasses.dexfile. These additions can quickly accumulate for complex systems or shared libraries. If possible, consider using the@IntDefannotation and ProGuardto strip enumerations out and convert them to integers. This type conversion preserves all of the type safety benefits of enums.
避免使用枚举类型
增加一个枚举类能够增加你应用的classes.dex文件大约1到1.4KB的大小。建议使用@IntDefannotation。
那么到正式项目中,
- 在使用混淆的情况下增加一个枚举具体会对APK增加多大的大小?
- 如果只创建一个枚举类,增加枚举中成员的个数,对
classes.dex的影响有多大? - 添加一个枚举类,到底增加了哪些代码?
- 在实际开发中,到底应不应该使用枚举类代替常量的定义?
- 对需要定义多个常量的开发场景,应该如果编写代码?
带着上面的问题,通过测试工程,一一解答。
一、在使用混淆的情况下增加一个枚举具体增加多大的大小?
1、 通过Android Studio创建一个默认项目,解压获取classes.dex文件,大小为1342700bytes,如下图:

2、 添加一个常量类,在代码中进行引用,增加代码如下:
// 常量类
public class Apple {
public static final int FIRST = 1;
public static final int SECOND = 2;
public static final int THREE = 3;
public static final int FOUR = 4;
public static final int FIVE = 5;
}
// MainActivity.class
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
Log.d("s", Apple.FIRST.toString())
}
重新获取classes.dex文件,大小为1342720bytes,如下图:

相对原始项目: 增加20bytes大小,影响非常小。
3、添加一个只有一个元素的枚举类,在代码中引用,另外删除掉上面常量类代码,增加代码如下:
enum Apple {
FIRST
}
// 引用如上面的代码,此处省略
重新获取classes.dex文件,大小为1343128bytes,如下图:

相对原始项目: 增加
428bytes大小,是增加一个常量类增加classes.dex文件大小的21.4倍
结论一: 在有混淆的情况下,增加一个枚举类,classes.dex文件大约增加0.5KB的大小。
二、如果只创建一个枚举类,增加枚举中成员的个数,对classes.dex的影响有多大?
1、 在1.3的基础上,对Apple枚举类中多添加几个元素,代码如下:
enum Apple {
FIRST,
SECOND,
THREE,
FOUR,
FIVE,
}
// 引用如上面的代码,此处省略
重新获取classes.dex文件,大小为134332bytes,如下图:

相对于只有一个元素的枚举类:增加204bytes,平均每增加一个增加50bytes。
结论二: 如果已经存在一个枚举类,向其中,每增加一个元素,大约增加50bytes,影响并不很大。
三、添加一个枚举类,到底增加了哪些代码?
那么,增加一个枚举类,到底增加了哪些元素?通过反编译APK文件,我们能够获取到其中的答案。
通过Android Studio自带的apk compare工具,我们可以知道,每增加一个枚举,在classes.dex中多增加1个class,增加4个method,那么增加了什么类,增加了哪些方法?
通过反编译,贴出关键代码如下:
.class final enum Lcom/learn/enumtest/a;
.super Ljava/lang/Enum;
# annotations
.annotation system Ldalvik/annotation/Signature;
value = {
"Ljava/lang/Enum<",
"Lcom/learn/enumtest/a;",
">;"
}
.end annotation
# static fields
.field public static final enum a:Lcom/learn/enumtest/a;
.field public static final enum b:Lcom/learn/enumtest/a;
.field public static final enum c:Lcom/learn/enumtest/a;
.field public static final enum d:Lcom/learn/enumtest/a;
.field public static final enum e:Lcom/learn/enumtest/a;
.field private static final synthetic f:[Lcom/learn/enumtest/a;
# direct methods
.method static constructor <clinit>()V
.locals 7
new-instance v0, Lcom/learn/enumtest/a;
const-string v1, "FIRST"
const/4 v2, 0x0
const-string v1, "SECOND"
const/4 v3, 0x1
const-string v1, "THREE"
const/4 v4, 0x2
const-string v1, "FOUR"
const/4 v5, 0x3
const-string v1, "FIVE"
return-void
// 省略掉其中部分代码
.end method
.method private constructor <init>(Ljava/lang/String;I)V
.locals 0
.annotation system Ldalvik/annotation/Signature;
value = {
"()V"
}
.end annotation
invoke-direct {p0, p1, p2}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V
return-void
.end method
.method public static valueOf(Ljava/lang/String;)Lcom/learn/enumtest/a;
.locals 1
const-class v0, Lcom/learn/enumtest/a;
invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
move-result-object p0
check-cast p0, Lcom/learn/enumtest/a;
return-object p0
.end method
.method public static values()[Lcom/learn/enumtest/a;
.locals 1
sget-object v0, Lcom/learn/enumtest/a;->f:[Lcom/learn/enumtest/a;
invoke-virtual {v0}, [Lcom/learn/enumtest/a;->clone()Ljava/lang/Object;
move-result-object v0
check-cast v0, [Lcom/learn/enumtest/a;
return-object v0
.end method
从反编译代码中,每增加一个枚举类,无论是单独的枚举,还是内部嵌套枚举,都会向classes.dex文件中新增加一个java类,类中增加两个构造函数,增加一个valueof函数,增加一个values函数
结论三: 添加一个枚举,在程序编译期,会自动增加有两个构造函数的Java类,同时生成values、valueof两个函数,方便程序的调用。
四、 在实际开发中,到底应不应该使用枚举类代替常量的定义?
通过上述比较,我们可以得出结论,使用枚举对classes.dex文件大小的影响,是直接定义常量的21.4倍,所以为了APK包的大小,尽力少使用枚举,除非在需要常量与资源对应的情况下。
五、 对需要定义多个常量的开发场景,应该如果编写代码?
推荐使用@intDef,代码如下:
public class Apple {
@IntDef({FIRST, SECOND, THREE, FOUR, FIVE})
public @interface State {
FIRST = 0;
SECOND = 1;
THREE = 2;
FOUR = 3;
FIVE = 4;
}
private int mState;
public void setState(@State int state) {
mState = state;
}
@State
public int getState() {
return mSate;
}
}
@interface注解修饰State,不需要在写public static final int进行修饰,@IntDef注解定义State都包含哪些值,如果不在取值范围内,在编译期会报红提醒,防止setState被随便设置参数的问题,代替枚举优势,同时对APK包大小的增加非常小。
枚举和常量的比较,就写到这里,如果有任何问题,欢迎留言。