Подтвердить что ты не робот

Сохранить перечисление в SharedPreference

Мне было интересно, какой общий метод используется для сохранения 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);

Мне было интересно, это хороший способ? Есть ли лучший способ?

4b9b3361

Ответ 1

Вы можете связать свои перечисления с целыми числами и сохранить простой int, посмотрите:

Вставить Int для перечисления в Java (второй ответ) - также вы можете сделать enumToInt

Ответ 2

Вы можете использовать простую 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

Ответ 3

Это та самая проблема, с которой сталкиваются разработчики 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)).

Этот подход требует немного дополнительного кода, но, на мой взгляд, это наиболее надежное решение.

Ответ 4

Учитывая все вышеперечисленные варианты, я решил использовать другое решение проблемы.

Мне нужен только набор значений, без какой-либо логики. В этом случае вместо использования 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();
  }

И вот официальный документ

Ответ 5

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

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