¿Debo evitar estrictamente el uso de enumeraciones en Android?

Solía ​​definir un conjunto de constantes relacionadas como las teclas de Bundle juntas en una interfaz como la siguiente:

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

Esto me proporciona una forma más agradable de agrupar las constantes relacionadas y usarlas haciendo una importación estática (no implementa). Sé que Android Framework también usa las constantes de la misma manera que Toast.LENTH_LONG , View.GONE .

Sin embargo, a menudo siento que los Java Enums proporcionan una forma mucho mejor y poderosa de representar la constante.

¿Pero hay un problema de rendimiento en el uso de enums en Android ?

Con un poco de investigación terminé en confusión. A partir de la pregunta “Evite los enums donde solo necesita Ints” eliminados de los consejos de rendimiento de Android, está claro que Google eliminó “Evitar enumeraciones” de sus consejos de rendimiento, pero a partir de sus documentos oficiales de formación. Enumeraciones a menudo requieren más del doble de memoria que las constantes estáticas. Debe evitar estrictamente el uso de enumeraciones en Android. “ ¿Esto todavía se mantiene bien? (Por ejemplo, en las versiones de Java posteriores a la 1.6)

Un problema más que observé es enviar enums través de intents utilizando Bundle . Debería enviarlos mediante serialización (es decir, putSerializable() , que creo que es una operación costosa en comparación con el método putString() primitivo, aunque enums proporciona de forma gratuita).

¿Puede alguien aclarar cuál es la mejor manera de representar lo mismo en Android ? ¿Debo evitar estrictamente el uso de enums en Android ?

Use enum cuando necesite sus características. No lo evites estrictamente .

Java enum es más potente, pero si no necesita sus características, use constantes, ocupan menos espacio y pueden ser primitivas.

Cuándo usar enum:

  • comprobación de tipos: puede aceptar solo los valores enumerados, y no son continuos (consulte más abajo lo que llamo continuo aquí)
  • Sobrecarga de métodos: cada constante enum tiene su propia implementación de un método

     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); } 
  • más datos: su única constante contiene más de una información que no se puede poner en una variable

  • datos complicados: sus métodos de necesidad constante para operar con los datos

Cuándo no usar enum:

  • puede aceptar todos los valores de un tipo, y sus constantes contienen solo los más utilizados
  • puedes aceptar datos continuos

     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); } ... } } 
  • para nombres (como en tu ejemplo)
  • para todo lo demás que realmente no necesita una enumeración

Los mensajes ocupan más espacio

  • una sola referencia a una constante enum ocupa 4 bytes
  • cada constante enum ocupa el espacio que es una sum de los tamaños de sus campos alineados a 8 bytes + sobrecarga del objeto
  • la clase enum en sí ocupa un poco de espacio

Las constantes ocupan menos espacio

  • una constante no tiene una referencia, por lo que es un dato puro (incluso si es una referencia, entonces la instancia enum sería una referencia a otra referencia)
  • las constantes se pueden agregar a una clase existente; no es necesario agregar otra clase
  • las constantes pueden estar en línea; trae características extendidas en tiempo de comstackción (como comprobación nula, búsqueda de código muerto, etc.)

Si las enumeraciones simplemente tienen valores, debe intentar usar IntDef / StringDef, como se muestra aquí:

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

Ejemplo: en lugar de:

 enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

tu usas:

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

y en la función que lo tiene como un parámetro / valor devuelto, use:

 @NavigationMode public abstract int getNavigationMode(); public abstract void setNavigationMode(@NavigationMode int mode); 

En caso de que la enumeración sea compleja, use una enumeración. No está tan mal.

Para comparar enums vs valores constantes, debe leer aquí:

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

Su ejemplo es de una enumeración con 2 valores. Toma 1112 bytes en el archivo dex en comparación con 128 bytes cuando se usan enteros constantes. Tiene sentido, ya que las enumeraciones son clases reales, a diferencia de cómo funciona en C / C ++.

¿Debo evitar estrictamente el uso de enumeraciones en Android?

No. ” Estrictamente ” significa que son tan malos que no deberían usarse en absoluto. Es posible que surjan problemas de rendimiento en una situación extrema como muchas muchas muchas (miles o millones de) operaciones con enumeraciones (consecutivas en el hilo de la interfaz de usuario). Mucho más comunes son las operaciones de E / S de red que deberían ocurrir estrictamente en una cadena de fondo. El uso más común de las enumeraciones es probablemente algún tipo de verificación de tipo: si un objeto es esto o aquello que es tan rápido, no podrá notar la diferencia entre una sola comparación de enumeraciones y una comparación de números enteros.

¿Puede alguien aclarar cuál es la mejor manera de representar lo mismo en Android?

No hay una regla general para esto. Use lo que sea que funcione para usted y lo ayude a preparar su aplicación. Optimice más tarde: después de notar que hay un cuello de botella que ralentiza algunos aspectos de su aplicación.

Además de las respuestas anteriores, agregaría que si está usando Proguard (y definitivamente debe hacerlo para reducir el tamaño y ofuscar su código), sus Enums se convertirán automáticamente a @IntDef siempre que sea posible:

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

clase / unboxing / enum

Simplifica los tipos enum a las constantes enteras, siempre que sea posible.

Por lo tanto, si tiene algunos valores discretos y algún método debería permitir tomar solo estos valores y no otros del mismo tipo, entonces usaría Enum , porque Proguard hará que este manual funcione para optimizar el código para mí.

Y aquí hay una buena publicación sobre el uso de enumeraciones de Jake Wharton, échale un vistazo.

Como desarrollador de la biblioteca, reconozco estas pequeñas optimizaciones que deben hacerse ya que queremos tener tan poco impacto en el tamaño, la memoria y el rendimiento de la aplicación como sea posible. Pero es importante darse cuenta de que poner una […] enumeración en su API pública frente a los valores enteros, donde corresponda, está perfectamente bien. Conocer la diferencia para tomar decisiones informadas es lo importante

Dos hechos

1, Enum es una de las características más poderosas en JAVA.

2, el teléfono Android por lo general tiene MUCHA memoria.

Entonces mi respuesta es NO. Usaré Enum en Android.

Con Android P, google no tiene restricciones / objeciones al usar enumeraciones

La documentación ha cambiado donde antes se recomendaba ser cauteloso pero no lo menciona ahora. https://developer.android.com/reference/java/lang/Enum

Me gustaría agregar que no puede usar @Annotations cuando declara una Lista <> o un Mapa <> donde la clave o el valor corresponde a una de sus interfaces de anotación. Obtiene el error “Las anotaciones no están permitidas aquí”.

 enum Values { One, Two, Three } Map 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 myMap; // *** ERROR *** 

Entonces, cuando necesite empacarlo en una lista / mapa, use enum, ya que pueden agregarse, pero @annotated int / string groups no puede.