Должен ли я строго избегать использования перечислений на 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. Вы должны использовать это всякий раз, когда требуется.