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

nvi*_*tty 90 java enums android

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

public interface From{
    String LOGIN_SCREEN = "LoginSCreen";
    String NOTIFICATION = "Notification";
    String WIDGET = "widget";
}
Run Code Online (Sandbox Code Playgroud)

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

但是,我经常觉得Java Enums提供更好,更强大的方式来表示常数.

但是,有没有在使用性能自动问题enumsAndroid

通过一些研究,我最终陷入困惑.从Android的性能提示Google中删除了"避免使用只需要Ints的枚举"的问题 吗?显然已经从其性能提示中删除了"避免枚举",但是从它的官方培训文档中了解内存开销部分它清楚地说:"枚举通常需要的内存是静态常量的两倍多.你应该严格避免在Android上使用枚举."这仍然很好吗?(Java在1.6之后的版本中说)

我观察到的一个问题,更是发送enumsintents使用Bundle我应该序列化给他们(即putSerializable(),我认为一个昂贵的操作比较原始的putString()方法沉绵enums提供它是免费的).

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

Kam*_*osz 107

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);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 更多数据 - 您的一个常量包含多个不能放在一个变量中的信息

  • 复杂的数据 - 您不断需要对数据进行操作的方法

何时使用枚举:

  • 您可以接受一种类型的所有值,并且您的常量仅包含最常用的值
  • 你可以接受连续的数据

    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);
            }
    
            ...
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 用于名称(如在您的示例中)
  • 对于其他一切真正不需要枚举的东西

Enums占据更多空间

  • 对枚举常量的单个引用占用4个字节
  • 每个枚举常量占用空间,该空间是其字段大小总和,对齐到8个字节+ 对象的开销
  • 枚举类本身占据了一些空间

常数占用的空间更少

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

  • 您可以使用@IntDef批注来模拟int常量的类型检查.这是支持Java Enums的一个较少的论据. (2认同)
  • @BladeCoder不,你不需要引用常量 (2认同)
  • @ w3bshark枚举如何促进反射的使用? (2认同)
  • 要记住的另一件事是来自标准空"Hello,world!"的堆转储.项目包括超过4,000个类和700,000个对象.即使你有一个包含数千个常量的非常大的枚举,你可以确定在Android框架本身的膨胀之后,性能影响可以忽略不计. (2认同)

and*_*per 57

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

http://tools.android.com/tech-docs/support-annotations

示例:代替:

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 
Run Code Online (Sandbox Code Playgroud)

你用:

@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;
Run Code Online (Sandbox Code Playgroud)

并在将其作为参数/返回值的函数中,使用:

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);
Run Code Online (Sandbox Code Playgroud)

如果枚举很复杂,请使用枚举.没那么糟糕.

要比较枚举与常量值,您应该在此处阅读:

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

他们的例子是一个包含2个值的枚举.在dex文件中需要1112个字节,而使用常量整数时需要128个字节.有道理,因为枚举是真正的类,而不是它在C/C++上的工作方式.

  • @ w3bshark如果您遇到性能问题,枚举不太可能是您应该考虑解决它们的第一件事.注释有自己的权衡:它们没有编译器支持,它们不能有自己的字段和方法,它们编写起来很乏味,你很容易忘记注释一个int,等等.它们不是一个更好的解决方案,它们只是在你需要时节省堆空间. (3认同)
  • @androiddeveloper好吧,如果你坚持严格的术语,错误我的意思是错误,这不是编译时错误(运行时或程序逻辑错误).你是在拖着我,还是真的没有区别它们和编译时错误的好处?这是一个讨论得很好的话题,请在网上查看.关于手表,Android确实包含更多设备,但最受限制的设备将是最低的共同点. (3认同)
  • @androiddeveloper我已经回复了这两个语句,再一次重复这些语句并没有使我所说的无效. (2认同)

sta*_*an0 11

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

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

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

对此没有一般的经验法则.使用适用于您的任何内容并帮助您准备好应用程序.稍后进行优化 - 在您发现有一个瓶颈会减慢应用程序的某些方面.


Gak*_*ket 11

除了之前的答案,我想补充一点,如果你使用的是Proguard(你绝对应该这样做以减小尺寸并混淆你的代码),那么你Enums将自动转换到@IntDef任何可能的地方:

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

类/拆箱/枚举

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

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

这里是 如何使用枚举从杰克沃顿好的帖子,看看它.

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


Ali*_*Ali 10

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

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