
在项目开发中,在需要定义多个常量的开发场景(如上传文件逻辑的状态回调),会犹豫是应该使用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。
那么到正式项目中,
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.classoverride fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) Log.d("s", Apple.FIRST.toString())}重新获取classes.dex文件,大小为1342720bytes,如下图:
使用Constants文件大小相对原始项目: 添加20bytes大小,影响非常小。
3、增加一个只有一个元素的枚举类,在代码中引用,另外删除掉上面常量类代码,添加代码如下:
enum Apple { FIRST}// 引用如上面的代码,此处省略重新获取classes.dex文件,大小为1343128bytes,如下图:
使用Enum增加一个元素文件大小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,如下图:
使用Enum增加5个元素文件大小相对于只有一个元素的枚举类:添加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包大小的添加非常小。
枚举和常量的比较,就写到这里,假如有任何疑问,欢迎留言。