Должен ли я строго избегать использования перечислений на Android?

Я использовал для определения набора связанных констант, таких как Bundle, ключи в интерфейсе, как показано ниже:

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

Это дает мне более удобный способ группировать связанные константы вместе и использовать их, создавая статический импорт (не реализует). Я знаю, что Android framework также использует константы так же, как Toast.LENTH_LONG, View.GONE.

Однако я часто чувствую, что Java Enums обеспечивает гораздо лучший и мощный способ представления константы.

Но есть ли проблема с исполнением при использовании enums на Android?

С небольшим количеством исследований я оказался в замешательстве. Из этого вопроса "Избегайте перечислений, где вам нужны только инты" , удалены из рекомендаций по производительности Android? ясно, что Google удалил "Избегайте перечислений" из своих рекомендаций по производительности, но из него официальный doc Внимательно прочитайте раздел "Накладные расходы на память" , в котором четко сказано: "В Enums часто требуется более чем вдвое больше памяти, чем статические константы. enums на Android." Это все еще хорошо? (скажем, в версиях Java после 1.6)

Еще одна проблема, которую я наблюдал, - отправить enums через intents с помощью Bundle, я должен отправить их путем сериализации (т.е. putSerializable(), что, по моему мнению, дорогостоящая операция по сравнению с примитивным методом putString(), eventhough enums предоставляет его бесплатно).

Может кто-то прояснить, какой из них лучше всего представить в Android? Должен ли я строго избегать использования enums на Android?

Ответ 1

Используйте 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);
    }
    
  • больше данных - одна ваша константа содержит более одной информации, которую нельзя поместить в одну переменную

  • сложные данные - ваши постоянные потребности в методах работы с данными

Когда не использовать enum:

  • вы можете принять все значения одного типа, а ваши константы содержат только эти наиболее используемые
  • вы можете принимать непрерывные данные

    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 байтов + накладные расходы объекта
  • сам класс enum занимает некоторое пространство

Константы занимают меньше места

  • константа не имеет ссылки, поэтому она является чистыми данными (даже если она является ссылкой, тогда экземпляр enum будет ссылкой на другую ссылку)
  • константы могут быть добавлены к существующему классу - нет необходимости добавлять другой класс
  • константы могут быть встроенными; он предоставляет расширенные функции во время компиляции (такие как проверка нуля, поиск мертвого кода и т.д.)

Ответ 2

Если перечисления просто имеют значения, вы должны попробовать использовать 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);

В случае, если перечисление является сложным, используйте перечисление. Это не так уж плохо.

Чтобы сравнить enums vs constant values, вы должны прочитать здесь:

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

Их пример представляет собой перечисление с 2 значениями. Он принимает 1112 байт в файле dex по сравнению с 128 байтами, когда используются постоянные целые числа. Имеет смысл, поскольку перечисления являются реальными классами, в отличие от того, как это работает на C/С++.

Ответ 3

Должен ли я строго избегать использования перечислений на Android?

Нет. "Строго" означает, что они такие плохие, их вообще нельзя использовать. Возможно, проблемы с производительностью могут возникать в экстремальной ситуации, например, во многих многих (тысячах или миллионах) операций с перечислениями (последовательно в потоке ui). Чаще всего работают сетевые операции ввода-вывода, которые должны строго выполняться в фоновом потоке. Наиболее распространенное использование перечислений - это, вероятно, какой-то тип проверки - является ли объект тем или иным, что так быстро, вы не сможете заметить разницу между единственным сопоставлением перечислений и сравнением целых чисел.

Может кто-нибудь, пожалуйста, уточните, какой из них лучше всего представлять на Android?

Для этого нет общего правила. Используйте все, что работает для вас, и поможет вам подготовить ваше приложение. Оптимизируйте позже - после того, как вы заметите, есть узкое место, которое замедляет некоторые аспекты вашего приложения.

Ответ 4

В дополнение к предыдущим ответам я хотел бы добавить, что если вы используете Proguard (и вам обязательно нужно сделать это, чтобы уменьшить размер и запутать ваш код), то ваши Enums будут автоматически преобразованы в @IntDef где это возможно:

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

класс/распаковка/перечисление

Упрощает перечисление типов до целочисленных констант, когда это возможно.

Поэтому, если у вас есть некоторые дискретные значения, и какой-то метод должен позволять принимать только эти значения, а не другие того же типа, я бы использовал Enum, потому что Proguard сделает эту ручную работу по оптимизации кода для меня.

И вот хороший пост об использовании перечислений от Джейка Уортона, взгляните на это.

Как разработчик библиотек, я осознаю эти небольшие оптимизации, которые должны быть выполнены, поскольку мы хотим, чтобы как можно меньшее влияние оказывалось на размер, память и производительность приложений. Но важно понимать, что [...] размещение перечисления в вашем публичном API вместо целочисленных значений, где это уместно, совершенно нормально. Знание различий в принятии обоснованных решений - вот что важно

Ответ 5

С Android P Google не имеет ограничений/возражений при использовании перечислений

Документация изменилась там, где раньше было рекомендовано быть осторожным, но сейчас это не упоминается. https://developer.android.com/reference/java/lang/Enum

Ответ 6

Два факта.

1, Enum является одной из самых мощных функций в JAVA.

2, телефон Android обычно имеет много памяти.

Итак, мой ответ НЕТ. Я буду использовать Enum в Android.

Ответ 7

Мне нравится добавлять, что вы не можете использовать @Annotations, когда вы объявляете List < > или Map < > где либо ключ, либо значение имеет один из ваших аннотационных интерфейсов. Вы получаете ошибку "Аннотации здесь не разрешены".

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 не могут.

Ответ 8

Enum - мощная особенность Java. Вы должны использовать это всякий раз, когда требуется.