我应该严格避免在Android上使用枚举吗?

时间:2015-03-21 14:37:32

标签: java android enums

我过去常常在如下界面中定义一组相关的常量,如Bundle个键:

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

这为我提供了一种更好的方法来将相关常量组合在一起,并通过进行静态导入(而不是实现)来使用它们。我知道Android框架也以Toast.LENTH_LONGView.GONE的相同方式使用常量。

但是,我经常觉得Java Enums提供了更好,更强大的方法来表示常量。

但在enums上使用Android是否存在性能问题?

通过一些研究,我最终感到困惑。从这个问题 "Avoid Enums Where You Only Need Ints” removed from Android's performance tips?很明显,Google已从其效果提示中删除“避免枚举”,但从其官方培训文档Be aware of memory overhead部分清楚地说明了: “枚举通常需要的内存是静态常量的两倍以上。你应该严格避免在Android上使用枚举。”这仍然很好吗? (比如在1.6之后的Java版本中)

我观察到的另一个问题是使用enumsintents之间发送Bundle我应该通过序列化发送它们(即putSerializable(),我认为比较昂贵的操作对于原始putString()方法,尽管enums免费提供它。

有人可以澄清哪一个是Android代表同一个人的最佳方式吗?我应该严格避免在enums上使用Android吗?

7 个答案:

答案 0 :(得分:102)

在需要其功能时使用enum请不要严格

Java枚举更强大,但如果你不需要它的功能,使用常量,它们占用的空间更少,而且它们本身就可以是原始的。

何时使用枚举:

  • 类型检查 - 您可以接受列出的值,并且它们不是连续的(请参阅下面我称之为连续的内容)
  • 方法重载 - 每个枚举常量都有自己的方法实现

    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);
            }
    
            ...
        }
    }
    
  • 代表名字(如你的例子)
  • 用于其他所有不需要枚举的内容

Enums占用更多空间

  • 对枚举常量的单个引用占用 4个字节
  • 每个枚举常数占据的空间是其字段的总和&#39;尺寸对齐到8字节+ 对象的开销
  • enum类本身占用了一些空间

常量占用较少的空间

  • 常量不具有引用,因此它是纯数据(即使它是引用,然后枚举实例将是对另一个引用的引用)
  • 常量可以添加到现有类中 - 不需要添加另一个类
  • 常量可以内联;它带来了扩展的编译时功能(例如空检查,查找死代码等)。

答案 1 :(得分:56)

如果枚举只有值,你应该尝试使用IntDef / StringDef,如下所示:

http://tools.android.com/tech-docs/support-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个值的枚举。在dex文件中需要1112个字节,而使用常量整数时需要128个字节。有道理,因为枚举是真正的类,而不是它在C / C ++上的工作方式。

答案 2 :(得分:11)

  

我应该严格避免在Android上使用枚举吗?

没有。 &#34; 严格&#34;意味着他们是如此糟糕,他们根本不应该被使用。可能在极端情况下会出现性能问题,例如许多(数千或数百万)带枚举的操作(在ui线程上连续)。更常见的是在后台线程中严格发生的网络I / O操作。 枚举的最常见用法可能是某种类型的检查 - 对象是这个还是 这是如此之快,以至于你无法注意到枚举的单一比较与整数比较之间的差异。

  

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

这没有一般的经验法则。使用适用于您的任何内容并帮助您准备好应用程序。稍后进行优化 - 在您发现存在导致应用程序某些方面变慢的瓶颈之后。

答案 3 :(得分:10)

除了之前的答案之外,我想补充一点,如果您使用Proguard(并且您一定要这样做以减小尺寸并模糊代码),那么您的Enums将自动转换为@IntDef }只要有可能:

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

  

<强>类/拆箱/枚举

     

尽可能将枚举类型简化为整数常量。

因此,如果你有一些离散值,某些方法应该只允许这个值而不是同一类型的其他值,那么我会使用Enum,因为Proguard将使这个手册优化代码我

here is关于使用杰克沃顿的点子的好帖子,请看一下。

  

作为一名图书馆开发人员,我认识到应该完成的这些小优化,因为我们希望对消费应用的大小,内存和性能产生尽可能小的影响。但重要的是要认识到在适当的情况下将公共API与整数值放在一起是完全正确的。知道差异以做出明智的决定是重要的

答案 4 :(得分:7)

使用Android P,谷歌在使用枚举时没有任何限制/异议

在建议谨慎之前,文档已经发生了变化,但现在还没有提及。 https://developer.android.com/reference/java/lang/Enum

答案 5 :(得分:3)

两个事实。

1,Enum是JAVA中最强大的功能之一。

2,Android手机通常有很多内存。

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

答案 6 :(得分:2)

我想补充一点,当您声明List&lt;&gt;时,您无法使用@Annotations。或地图&lt;&gt;其中一个键或值是您的一个注释接口。 您收到错误&#34;此处不允许使用注释&#34;。

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 ***

因此,当您需要将其打包到列表/映射中时,请使用枚举,因为它们可以添加,但@annotated int / string groups不能。