簡體   English   中英

我應該嚴格避免在 Android 上使用枚舉嗎?

[英]Should I strictly avoid using enums on Android?

我曾經在如下界面中定義了一組相關的常量,例如Bundle鍵:

public interface From{
    String LOGIN_SCREEN = "LoginSCreen";
    String NOTIFICATION = "Notification";
    String WIDGET = "widget";
}

這為我提供了一種更好的方法來將相關常量組合在一起,並通過進行靜態導入(而不是實現)來使用它們。 我知道Android框架也以與Toast.LENTH_LONGView.GONE相同的方式使用常量。

然而,我經常覺得Java Enums提供了更好、更強大的方式來表示常量。

但是在Android上使用enums是否存在性能問題?

經過一些研究,我最終陷入了困惑。 從這個問題“Avoid Enums Where You Only Need Ints”中刪除了Android的性能提示?很明顯Google已經從它的性能提示中刪除了“避免枚舉” ,但是從它的官方培訓文檔注意內存開銷部分它清楚地說: “枚舉通常需要比靜態常量多兩倍的內存。 你應該嚴格避免在 Android 上使用枚舉。”這仍然有效嗎?(比如 1.6 之后的Java版本)

我觀察到的另一個問題是使用Bundleintents發送enums我應該通過序列化發送它們(即putSerializable() ,我認為與原始putString()方法相比,這是一種昂貴的操作,盡管enums是免費提供的)。

有人可以澄清哪一種是在Android表示相同的最佳方式嗎? 我應該嚴格避免在Android上使用enums嗎?

當您需要其功能時使用enum 不要嚴格避免它

Java enum 更強大,但如果你不需要它的特性,使用常量,它們占用更少的空間並且它們本身可以是原始的。

何時使用枚舉:

  • 類型檢查-你可以接受列出的值,而且他們是不連續的(參見下面的什么,我在這里稱之為連續
  • 方法重載 - 每個枚舉常量都有自己的方法實現

    public enum UnitConverter{ METERS{ @Override public double toMiles(final double meters){ return meters * 0.00062137D; } @Override public double toMeters(final double meters){ return meters; } }, MILES{ @Override public double toMiles(final double miles){ return miles; } @Override public double toMeters(final double miles){ return miles / 0.00062137D; } }; public abstract double toMiles(double unit); public abstract double toMeters(double unit); }
  • 更多數據 - 你的一個常量包含多個不能放在一個變量中的信息

  • 復雜的數據 - 您不斷需要對數據進行操作的方法

何時使用枚舉:

  • 您可以接受一種類型的所有值,並且您的常量僅包含這些最常用的
  • 你可以接受連續數據

    public class Month{ public static final int JANUARY = 1; public static final int FEBRUARY = 2; public static final int MARCH = 3; ... public static String getName(final int month){ if(month <= 0 || month > 12){ throw new IllegalArgumentException("Invalid month number: " + month); } ... } }
  • 對於名稱(如您的示例)
  • 對於真正不需要枚舉的其他所有內容

枚舉占用更多空間

  • 對枚舉常量的單個引用占用4 個字節
  • 每個枚舉常量占用的空間是其字段大小總和對齊到 8 個字節 +對象的開銷
  • 枚舉類本身占用了一些空間

常量占用更少的空間

  • 常量沒有引用,因此它是純數據(即使它是引用,枚舉實例也將是對另一個引用的引用)
  • 常量可以添加到現有的類 - 沒有必要添加另一個類
  • 常量可以被內聯; 它帶來了擴展的編譯時功能(例如空檢查、查找死代碼等)

如果枚舉只有值,您應該嘗試使用 IntDef/StringDef ,如下所示:

https://developer.android.com/studio/write/annotations.html#enum-annotations

示例:而不是:

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

你用:

@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
@Retention(RetentionPolicy.SOURCE)
public @interface NavigationMode {}

public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;

並在將其作為參數/返回值的函數中,使用:

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);

如果枚舉很復雜,請使用枚舉。 它沒有那么壞。

要比較枚舉與常量值,您應該在這里閱讀:

http://hsc.com/Blog/Best-Practices-For-Memory-Optimization-on-Android-1

他們的例子是一個有 2 個值的枚舉。 與使用常量整數時的 128 字節相比,它在 dex 文件中需要 1112 字節。 有道理,因為枚舉是真正的類,而不是它在 C/C++ 上的工作方式。

除了以前的答案,我想補充一點,如果你正在使用Proguard的(和你一定要做到這一點,以減少大小和模糊處理的代碼),那么你的Enums將被自動轉換為@IntDef無論它是可能的:

https://www.guardsquare.com/en/proguard/manual/optimizations

類/拆箱/枚舉

盡可能將枚舉類型簡化為整數常量。

因此,如果您有一些離散值並且某些方法應該只允許采用這些值而不是其他相同類型的值,那么我將使用Enum ,因為 Proguard 將為我進行優化代碼的手動工作。

這是一篇關於使用 Jake Wharton 枚舉的好文章,請看一看。

作為庫開發人員,我認為應該進行這些小的優化,因為我們希望對消費應用程序的大小、內存和性能產生盡可能小的影響。 但重要的是要意識到 [...] 在你的公共 API 中放置一個枚舉而不是整數值是完全沒問題的。 了解差異以做出明智的決定是重要的

使用 Android P,谷歌在使用枚舉方面沒有限制/反對

文檔已經更改了之前建議謹慎的地方,但現在沒有提及。 https://developer.android.com/reference/java/lang/Enum

我應該嚴格避免在 Android 上使用枚舉嗎?

不。“嚴格”意味着它們太糟糕了,根本不應該使用它們。 在極端情況下可能會出現性能問題,例如使用枚舉(在 ui 線程上連續進行)的許多(數千或數百萬)操作。 更常見的是應該嚴格發生在后台線程中的網絡 I/O 操作。 枚舉最常見的用法可能是某種類型的檢查——一個對象是這個還是那個,它太快了,你將無法注意到枚舉的單個比較和整數的比較之間的區別。

有人可以澄清哪一種是在Android中表示相同的最佳方式嗎?

對此沒有一般的經驗法則。 使用適合您的任何東西並幫助您准備好您的應用程序。 稍后優化 - 在您注意到有一個瓶頸會減慢您的應用程序的某些方面。

兩個事實。

1、枚舉是JAVA中最強大的特性之一。

2、Android手機通常有很多內存。

所以我的答案是否定的。 我將在 Android 中使用 Enum。

我想補充一點,當您聲明 List<> 或 Map<> 時不能使用 @Annotations,其中鍵或值是您的注釋接口之一。 您收到錯誤“此處不允許使用注釋”。

enum Values { One, Two, Three }
Map<String, Values> myMap;    // This works

// ... but ...
public static final int ONE = 1;
public static final int TWO = 2;
public static final int THREE = 3;

@Retention(RetentionPolicy.SOURCE)
@IntDef({ONE, TWO, THREE})
public @interface Values {}

Map<String, @Values Integer> myMap;    // *** ERROR ***

因此,當您需要將其打包到列表/映射中時,請使用 enum,因為它們可以添加,但 @annotated int/string 組不能。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM