繁体   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