如需轉載請評論或簡信,并注明出處,未經(jīng)允許不得轉載
目錄
前言
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中,使用枚舉類可以保證類型安全和提高代碼可讀性。無論需求如何變化求晶,比如枚舉常量增加一個數(shù)據(jù)崖蜜,或是增加一條狀態(tài)浊仆,都可以很方便的實現(xiàn),比直接在全局常量類里定義狀態(tài)來的便捷
static與Enum比較
第一步:有一個Android Project豫领,編譯后生成的dex的體積為3,465,228byte
第二步:新增創(chuàng)建一個類抡柿,如下所示,編譯后生成的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;
}
}
}
- 第三步等恐,改寫第二步中創(chuàng)建的類洲劣,如下所示,編譯后生成的dex的體積為3,466,568 byte(比初始增加1340byte课蔬,比使用
static
增加1036byte)
初步結論:使用Enum會比static增加2-3倍的byte囱稽,從而增加apk的體積
Enum源碼分析
我們創(chuàng)建了一個枚舉類Color
public enum Color {
RED, GREEN, BLACK, YELLOW
}
編譯項目,在app/build/intermediates/javac/debug/classes/項目名稱
目錄下二跋,可以找到Color.class文件战惊,執(zhí)行javap -c Color.class
命令對class文件進行反編譯,結果如下所示
Compiled from "Color.java"
public final class com.geekholt.kotlinandjavademo.Color extends java.lang.Enum<com.geekholt.kotlinandjavademo.Color> {
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 "<init>":(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 "<init>":(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 "<init>":(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 "<init>":(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
}
把上面的字節(jié)碼文件翻譯成java大致如下所示
public final class Color extends java.lang.Enum<Color> {
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
文件時不保留枚舉注解數(shù)據(jù) -
使用方法如下莫杈, 這樣外界就無法傳遞
Color
之外的成員作為參數(shù)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
優(yōu)化到整數(shù)對象互例。
結論
在android中使用枚舉類不僅會增加apk體積,同時也會增加運行時內存筝闹,所以在架構設計上還是要慎用枚舉類媳叨。如果希望進行編譯期類型檢查可以使用@IntDef
和@StringDef
類保證類型安全