简体   繁体   English

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

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

I used to define a set of related constants like Bundle keys together in an interface like below:我曾经在如下界面中定义了一组相关的常量,例如Bundle键:

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

This provides me a nicer way to group related constants together and used them by making a static import (not implements).这为我提供了一种更好的方法来将相关常量组合在一起,并通过进行静态导入(而不是实现)来使用它们。 I know Android framework also uses the constants in same way like Toast.LENTH_LONG , View.GONE .我知道Android框架也以与Toast.LENTH_LONGView.GONE相同的方式使用常量。

However, I often feel that the Java Enums provide much better and powerful way to represent the constant.然而,我经常觉得Java Enums提供了更好、更强大的方式来表示常量。

But is there a performence issue in using enums on Android ?但是在Android上使用enums是否存在性能问题?

With a bit of research I ended up in confusion.经过一些研究,我最终陷入了困惑。 From this question "Avoid Enums Where You Only Need Ints” removed from Android's performance tips? it's clear that Google has removed "Avoid enums" from its performance tips, but from it's official training docs Be aware of memory overhead section it clearly says: "Enums often require more than twice as much memory as static constants.从这个问题“Avoid Enums Where You Only Need Ints”中删除了Android的性能提示?很明显Google已经从它的性能提示中删除了“避免枚举” ,但是从它的官方培训文档注意内存开销部分它清楚地说: “枚举通常需要比静态常量多两倍的内存。 You should strictly avoid using enums on Android." Is this still holds good? (say in Java versions after 1.6)你应该严格避免在 Android 上使用枚举。”这仍然有效吗?(比如 1.6 之后的Java版本)

One more issue that I observed is to send enums across intents using Bundle I should send them by serializing (ie putSerializable() , that I think an expensive operation compared to primitive putString() method, eventhough enums provides it for free).我观察到的另一个问题是使用Bundleintents发送enums我应该通过序列化发送它们(即putSerializable() ,我认为与原始putString()方法相比,这是一种昂贵的操作,尽管enums是免费提供的)。

Can someone please clarify which one is the best way to represent the same in Android ?有人可以澄清哪一种是在Android表示相同的最佳方式吗? Should I strictly avoid using enums on Android ?我应该严格避免在Android上使用enums吗?

Use enum when you need its features.当您需要其功能时使用enum Don't avoid it strictly .不要严格避免它

Java enum is more powerful, but if you don't need its features, use constants, they occupy less space and they can be primitive itself. Java enum 更强大,但如果你不需要它的特性,使用常量,它们占用更少的空间并且它们本身可以是原始的。

When to use enum:何时使用枚举:

  • type checking - you can accept only listed values, and they are not continuous (see below what I call continuous here)类型检查-你可以接受列出的值,而且他们是不连续的(参见下面的什么,我在这里称之为连续
  • method overloading - every enum constant has its own implementation of a method方法重载 - 每个枚举常量都有自己的方法实现

    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); }
  • more data - your one constant contains more than one information that cannot be put in one variable更多数据 - 你的一个常量包含多个不能放在一个变量中的信息

  • complicated data - your constant need methods to operate on the data复杂的数据 - 您不断需要对数据进行操作的方法

When not to use enum:何时使用枚举:

  • you can accept all values of one type, and your constants contain only these most used您可以接受一种类型的所有值,并且您的常量仅包含这些最常用的
  • you can accept continuous data你可以接受连续数据

    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); } ... } }
  • for names (like in your example)对于名称(如您的示例)
  • for everything else that really doesn't need an enum对于真正不需要枚举的其他所有内容

Enums occupy more space枚举占用更多空间

  • a single reference to an enum constant occupies 4 bytes对枚举常量的单个引用占用4 个字节
  • every enum constant occupies space that is a sum of its fields' sizes aligned to 8 bytes + overhead of the object每个枚举常量占用的空间是其字段大小总和对齐到 8 个字节 +对象的开销
  • the enum class itself occupies some space枚举类本身占用了一些空间

Constants occupy less space常量占用更少的空间

  • a constant doesn't have a reference so it's a pure data (even if it's a reference, then enum instance would be a reference to another reference)常量没有引用,因此它是纯数据(即使它是引用,枚举实例也将是对另一个引用的引用)
  • constants may be added to an existing class - it's not necessary to add another class常量可以添加到现有的类 - 没有必要添加另一个类
  • constants may be inlined;常量可以被内联; it brings extended compile-time features (such as null checking, finding dead code etc.)它带来了扩展的编译时功能(例如空检查、查找死代码等)

If the enums simply have values, you should try to use IntDef/StringDef , as shown here:如果枚举只有值,您应该尝试使用 IntDef/StringDef ,如下所示:

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

Example: instead of :示例:而不是:

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

you use:你用:

@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;

and in the function that has it as a parameter/returned value , use:并在将其作为参数/返回值的函数中,使用:

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);

In case the enum is complex, use an enum.如果枚举很复杂,请使用枚举。 It's not that bad.它没有那么坏。

To compare enums vs constant values, you should read here:要比较枚举与常量值,您应该在这里阅读:

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

Their example is of an enum with 2 values.他们的例子是一个有 2 个值的枚举。 It takes 1112 bytes in dex file compared to 128 bytes when constant integers are used .与使用常量整数时的 128 字节相比,它在 dex 文件中需要 1112 字节。 Makes sense, as enums are real classes, as opposed to how it works on C/C++ .有道理,因为枚举是真正的类,而不是它在 C/C++ 上的工作方式。

In addition to previous answers, I would add that if you are using Proguard (and you should definitely do it to reduce size and obfuscate your code), then your Enums will be automatically converted to @IntDef wherever it is possible:除了以前的答案,我想补充一点,如果你正在使用Proguard的(和你一定要做到这一点,以减少大小和模糊处理的代码),那么你的Enums将被自动转换为@IntDef无论它是可能的:

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

class/unboxing/enum类/拆箱/枚举

Simplifies enum types to integer constants, whenever possible.尽可能将枚举类型简化为整数常量。

Therefore, if you have some discrete values and some method should allow to take only this values and not others of the same type, then I would use Enum , because Proguard will make this manual work of optimizing code for me.因此,如果您有一些离散值并且某些方法应该只允许采用这些值而不是其他相同类型的值,那么我将使用Enum ,因为 Proguard 将为我进行优化代码的手动工作。

And here is a good post about using enums from Jake Wharton, take a look at it.这是一篇关于使用 Jake Wharton 枚举的好文章,请看一看。

As a library developer, I recognize these small optimizations that should be done as we want to have as little impact on the consuming app's size, memory, and performance as possible.作为库开发人员,我认为应该进行这些小的优化,因为我们希望对消费应用程序的大小、内存和性能产生尽可能小的影响。 But it's important to realize that [...] putting an enum in your public API vs. integer values where appropriate is perfectly fine.但重要的是要意识到 [...] 在你的公共 API 中放置一个枚举而不是整数值是完全没问题的。 Knowing the difference to make informed decisions is what's important了解差异以做出明智的决定是重要的

With Android P, google has no restriction/objection in using enums使用 Android P,谷歌在使用枚举方面没有限制/反对

The documentation has changed where before it was recommended to be cautious but it doesn't mention it now.文档已经更改了之前建议谨慎的地方,但现在没有提及。 https://developer.android.com/reference/java/lang/Enum https://developer.android.com/reference/java/lang/Enum

Should I strictly avoid using enums on Android?我应该严格避免在 Android 上使用枚举吗?

No. " Strictly " means they are so bad, they should not be used at all.不。“严格”意味着它们太糟糕了,根本不应该使用它们。 Possibly a performance issues might arise in an extreme situation like many many many (thousands or millions of) operations with enums (consecutive on the ui thread).在极端情况下可能会出现性能问题,例如使用枚举(在 ui 线程上连续进行)的许多(数千或数百万)操作。 Far more common are the network I/O operations that should strictly happen in a background thread.更常见的是应该严格发生在后台线程中的网络 I/O 操作。 The most common usage of enums is probably some kind of type check - whether an object is this or that which is so fast you won't be able to notice a difference between a single comparison of enums and a comparison of integers.枚举最常见的用法可能是某种类型的检查——一个对象是这个还是那个,它太快了,你将无法注意到枚举的单个比较和整数的比较之间的区别。

Can someone please clarify which one is the best way to represent the same in Android?有人可以澄清哪一种是在Android中表示相同的最佳方式吗?

There is no general rule of thumb for this.对此没有一般的经验法则。 Use whatever works for you and helps you get your app ready.使用适合您的任何东西并帮助您准备好您的应用程序。 Optimize later - after you notice there's a bottleneck that slows some aspect of your app.稍后优化 - 在您注意到有一个瓶颈会减慢您的应用程序的某些方面。

Two facts.两个事实。

1, Enum is one of the most powerful feature in JAVA. 1、枚举是JAVA中最强大的特性之一。

2, Android phone usually has a LOT of memory. 2、Android手机通常有很多内存。

So my answer is NO.所以我的答案是否定的。 I will use Enum in Android.我将在 Android 中使用 Enum。

I like to add, that you can not use @Annotations when you declare a List<> or Map<> where either key or value is of one of your annotation interfaces.我想补充一点,当您声明 List<> 或 Map<> 时不能使用 @Annotations,其中键或值是您的注释接口之一。 You get the error "Annotations are not allowed here".您收到错误“此处不允许使用注释”。

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

So when you need to pack it into a list/map, use enum, as they can be added, but @annotated int/string groups can not.因此,当您需要将其打包到列表/映射中时,请使用 enum,因为它们可以添加,但 @annotated int/string 组不能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM