Ответ 1
Вы можете связать свои перечисления с целыми числами и сохранить простой int, посмотрите:
Вставить Int для перечисления в Java (второй ответ) - также вы можете сделать enumToInt
Мне было интересно, какой общий метод используется для сохранения Enum
до SharedPrefereces
? В настоящее время я использую gson
для преобразования enum в String, а затем сохраняю его в SharedPrefereces
.
Gson gson = new Gson();
// country is an enum.
String json_country = gson.toJson(country);
sharedPreferences.edit().putString(COUNTRY, json_country);
Мне было интересно, это хороший способ? Есть ли лучший способ?
Вы можете связать свои перечисления с целыми числами и сохранить простой int, посмотрите:
Вставить Int для перечисления в Java (второй ответ) - также вы можете сделать enumToInt
Вы можете использовать простую String для него, а затем извлечь значение, используя метод valueOf. Вот пример:
public enum MyEnum {
ENUM1, ENUM2, ENUM3, ENUM4;
public static MyEnum toMyEnum (String myEnumString) {
try {
return valueOf(myEnumString);
} catch (Exception ex) {
// For error cases
return ENUM1;
}
}
}
public void setMyEnum(Context context, MyEnum myEnum) {
SharedPreferences sp = context.getPreferences(this.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("MyEnum", myEnum.toString());
editor.commit();
}
public MyEnum getMyEnum(Context context) {
SharedPreferences sp = context.getPreferences(this.MODE_PRIVATE);
String myEnumString = sp.getString("MyEnum", MyEnum.ENUM1.toString());
return MyEnum.toMyEnum(myEnumString);
}
Вот пример кода, который вы можете увидеть, как он работает. https://github.com/jiahaoliuliu/SavingEnumToSharedPreferences
Это та самая проблема, с которой сталкиваются разработчики Enterprise Java при сохранении перечисления в базе данных. Проблема с существующими ответами заключается в том, что они хрупкие, а не рефакторинговые. Вот почему (с альтернативой внизу).
Использование методов enum toString()
и valueOf()
означает, что значение перечисления не может быть переименовано. Предположим, что у меня есть перечисление VehicleType
со значениями CAR
и TRUCK
. Если я сохраню строку "TRUCK" в качестве значения предпочтения, а затем переименуйте VehicleType.TRUCK
в VehicleType.PICKUP_TRUCK
в следующей версии моего приложения, сохраненная строка больше не имеет смысла, а valueOf()
будет вызывать IllegalArgumentException
.
Использование порядкового номера значения означает, что значения перечисления не могут быть переупорядочены или ваши сохраненные значения больше не совпадут. Этот сценарий, вероятно, хуже, потому что ваше приложение будет продолжать работать, но может вести себя непредсказуемыми способами, что затрудняет отслеживание проблемы, как только оно наконец будет замечено и, вероятно, невозможно будет исправить. То же самое верно для добавления нового значения где угодно, кроме конца. Если я добавил новое значение MOTORCYCLE
вверху, a CAR
, сохраненное как его порядковый номер (0) в предыдущей версии приложения, вернется как MOTORCYCLE
после обновления и TRUCK
(порядковый номер 1) станет CAR
.
Альтернативой, которую я использую, является добавление поля final
в перечисление и использование его значения, например:
public enum VehicleType {
CAR("C"),
TRUCK("T");
private final String code;
private static final Map<String,VehicleType> valuesByCode;
static {
valuesByCode = new HashMap<>(values().length);
for(VehicleType value : values()) {
valuesByCode.put(value.code, value);
}
}
VehicleType(String code) {
this.code = code;
}
public static VehicleType lookupByCode(String code) {
return valuesByCode.get(code);
}
public String getCode() {
return code;
}
}
Сохраните значение, используя что-то вроде preferences.putString("vehicle_type", vehicleType.getCode())
, и извлеките его, используя что-то вроде vehicleType = VehicleType.lookupByCode(preferences.getString("vehicle_type", null))
.
Этот подход требует немного дополнительного кода, но, на мой взгляд, это наиболее надежное решение.
Учитывая все вышеперечисленные варианты, я решил использовать другое решение проблемы.
Мне нужен только набор значений, без какой-либо логики. В этом случае вместо использования enum
можно использовать такие инструменты, как @IntDef
и @StringDef
.
Решение безопасно рефакторировать как с точки зрения имен переменных, так и с помощью переменных. Единственное, что не следует изменять - фактические значения. Однако инструменты рефакторинга по умолчанию не будут просить вас изменить их в случае переименования, поэтому сделать это просто не так просто (и @IntDef
лучше с этой точки зрения, чем @StringDef
).
@Retention(SOURCE)
@IntDef({NOT_REQUESTED, GIVEN, PROHIBITED})
public @interface CustomPermission {}
public static final int NOT_REQUESTED = -1;
public static final int PROHIBITED = 0;
public static final int GIVEN = 1;
@CustomPermission
public int getPermission() {
return getSharedPreferences(SHARED_PREFS_FILE)
.getInt(STATISTICS_COLLECTION_PERMISSION, NOT_REQUESTED);
}
public void setPermission(@CustomPermission int flag) {
getSharedPreferences(SHARED_PREFS_FILE)
.edit()
.putInt(STATISTICS_COLLECTION_PERMISSION, flag)
.apply();
}
И вот официальный документ
Вот хороший класс утилит, который я написал для этого. Он очень обобщен, чтобы соответствовать широкому спектру требований.
import android.content.Context;
import android.content.SharedPreferences;
public class PreferencesUtils
{
enum StorageMode {
/** Store enum by ordinal. */
ORDINAL,
/** Store enum by name. */
NAME
}
private static final String PREFERENCES_NAME = "your_prefs_name";
/**
* Put an enum in SharedPreferences with the given key.
* @param context The context
* @param key The preference key
* @param enumm The enum to store
* @param mode The mode to store the enum - by name or ordinal
*/
public static void putEnum(final Context context, final String key, final Enum enumm, final StorageMode mode)
{
final SharedPreferences sp = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
final SharedPreferences.Editor editor = sp.edit();
switch (mode)
{
default:
case ORDINAL:
editor.putInt(key, enumm.ordinal());
break;
case NAME:
editor.putString(key, enumm.name());
break;
}
editor.apply();
}
/**
* Get the enum stored in SharedPreferences with the given key.
* @param context The context
* @param key The preference key
* @param enumType The type of Enum stored
* @param defaultEnumm Enum returned if a preference stored with key does not exist. Can be null.
* @param mode The mode by which the enum was originally stored
* @param <T> The type of Enum stored
* @return The Enum stored as a preference with the given key
*/
public static <T extends Enum<T>> Enum getEnum(final Context context, final String key, final Class<T> enumType,
final Enum defaultEnumm, final StorageMode mode)
{
final SharedPreferences sp = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
final T[] values = enumType.getEnumConstants();
switch (mode)
{
default:
case ORDINAL:
final int ord = sp.getInt(key, -1);
return ord == -1 ? defaultEnumm : values[ord];
case NAME:
final String name = sp.getString(key, null);
for (final T value : values)
{
if (value.name().equals(name))
{
return value;
}
}
}
return defaultEnumm;
}
}