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

Лучшие рекомендации для Android SharedPreferences

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

PreferenceManager.getDefaultSharedPreferences(Context context)

Однако похоже, что это может быть опасно. Если у вас есть большое приложение, которое полагается на SharedPreferences, у вас может быть ключевое дублирование, особенно в случае использования сторонней библиотеки, которая также использует SharedPreferences. Мне кажется, что лучший призыв к использованию:

Context.getSharedPreferences(String name, int mode)

Таким образом, если у вас есть класс, который сильно зависит от SharedPreferences, вы можете создать файл настроек, который используется только вашим классом. Вы можете использовать полное имя класса, чтобы убедиться, что файл, скорее всего, не будет дублироваться кем-то другим.

Также на основе этого вопроса SO: Должен ли доступ к SharedPreferences сделать из потока пользовательского интерфейса?, кажется, что доступ к SharedPreferences должен выполняться из потока пользовательского интерфейса, что имеет смысл.

Есть ли другие рекомендации, которые разработчики Android должны знать при использовании SharedPreferences в своих приложениях?

4b9b3361

Ответ 1

Если у вас есть большое приложение, которое полагается на SharedPreferences, у вас может быть ключевое дублирование, особенно в случае использования какой-либо сторонней библиотеки, которая также использует SharedPreferences.

Библиотеки не должны использовать этот конкретный SharedPreferences. Значение по умолчанию SharedPreferences должно использоваться только приложением.

Таким образом, если у вас есть класс, который сильно зависит от SharedPreferences, вы можете создать файл настроек, который используется только вашим классом.

Вы, безусловно, можете это сделать. Я бы не стал на уровне приложения, поскольку основной причиной для SharedPreferences является их совместное использование среди компонентов в приложении. У команды разработчиков не должно быть проблем с управлением этим пространством имен, так же как у них не должно быть проблем с управлением именами классов, пакетов, ресурсов или других материалов на уровне проекта. Кроме того, по умолчанию SharedPreferences будет использоваться ваш PreferenceActivity.

Однако, возвращаясь к точке ваших библиотек, библиотеки многократного использования должны использовать отдельный SharedPreferences только для своей библиотеки. Я бы не основывал его на имени класса, потому что тогда вы один рефакторинг от взлома приложения. Вместо этого выберите уникальное имя (например, на основе имени библиотеки, например "com.commonsware.cwac.wakeful.WakefulIntentService"), но стабильного.

кажется, что доступ к SharedPreferences должен выполняться из потока пользовательского интерфейса, что имеет смысл.

В идеале, да. Недавно я выпустил SharedPreferencesLoader, который помогает с этим.

Есть ли другие рекомендации, которые разработчики Android должны знать при использовании SharedPreferences в своих приложениях?

Не переусердствуйте на них. Они хранятся в файлах XML и не являются транзакционными. База данных должна быть вашим основным хранилищем данных, особенно для данных, которые вы действительно не хотите потерять.

Ответ 2

Я написал небольшую статью, которую также можно найти здесь. Он описывает, что такое SharedPreferences:

Лучшая практика: SharedPreferences

Android предоставляет множество способов хранения данных приложения. Один из этих способов приводит нас к объекту SharedPreferences, который используется для хранения личных примитивных данных в парах ключ-значение.

Вся логика основана только на трех простых классах:

SharedPreferences

SharedPreferences является основным из них. Он отвечает за получение (анализ) хранимых данных, предоставляет интерфейс для получения объекта Editor и интерфейсы для добавления и удаления OnSharedPreferenceChangeListener

  • Для создания SharedPreferences вам понадобится объект Context (может быть приложением Context)
  • Метод getSharedPreferences анализирует файл настроек и создает для него объект Map
  • Вы можете создать его в нескольких режимах, предоставляемых Context. Настоятельно рекомендуется использовать MODE_PRIVATE, поскольку создание общедоступных/доступных для записи файлов очень опасно и может привести к дырам в безопасности приложений.

    // parse Preference file
    SharedPreferences preferences = context.getSharedPreferences("com.example.app", Context.MODE_PRIVATE);
    
    // get values from Map
    preferences.getBoolean("key", defaultValue)
    preferences.get..("key", defaultValue)
    
    // you can get all Map but be careful you must not modify the collection returned by this
    // method, or alter any of its contents.
    Map<String, ?> all = preferences.getAll();
    
    // get Editor object
    SharedPreferences.Editor editor = preferences.edit();
    
    //add on Change Listener
    preferences.registerOnSharedPreferenceChangeListener(mListener);
    
    //remove on Change Listener
    preferences.unregisterOnSharedPreferenceChangeListener(mListener);
    
    // listener example
    SharedPreferences.OnSharedPreferenceChangeListener mOnSharedPreferenceChangeListener
        = new SharedPreferences.OnSharedPreferenceChangeListener() {
      @Override
      public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
      }
    };
    

редактор

SharedPreferences.Editor - это интерфейс, используемый для изменения значений в объекте SharedPreferences. Все изменения, которые вы делаете в редакторе, пакетируются и не копируются обратно в исходные SharedPreferences пока вы не SharedPreferences commit() или apply()

  • Используйте простой интерфейс для ввода значений в Editor
  • Сохраняйте значения синхронно с commit() или асинхронно с apply что быстрее. На самом деле использование различных потоков с использованием commit() безопаснее. Вот почему я предпочитаю использовать commit().
  • Удалите одно значение с помощью remove() или удалите все значения с помощью clear()

    // get Editor object
    SharedPreferences.Editor editor = preferences.edit();
    
    // put values in editor
    editor.putBoolean("key", value);
    editor.put..("key", value);
    
    // remove single value by key
    editor.remove("key");
    
    // remove all values
    editor.clear();
    
    // commit your putted values to the SharedPreferences object synchronously
    // returns true if success
    boolean result = editor.commit();
    
    // do the same as commit() but asynchronously (faster but not safely)
    // returns nothing
    editor.apply();
    

Производительность и советы

  • SharedPreferences - это объект Singleton, поэтому вы можете легко получить столько ссылок, сколько хотите, он открывает файл только при первом вызове getSharedPreferences или создает только одну ссылку для него.

    // There are 1000 String values in preferences
    
    SharedPreferences first = context.getSharedPreferences("com.example.app", Context.MODE_PRIVATE);
    // call time = 4 milliseconds
    
    SharedPreferences second = context.getSharedPreferences("com.example.app", Context.MODE_PRIVATE);
    // call time = 0 milliseconds
    
    SharedPreferences third = context.getSharedPreferences("com.example.app", Context.MODE_PRIVATE);
    // call time = 0 milliseconds
    
  • Поскольку SharedPreferences является объектом Singleton, вы можете изменить любой из его экземпляров и не бояться, что их данные будут другими.

    first.edit().putInt("key",15).commit();
    
    int firstValue = first.getInt("key",0)); // firstValue is 15
    int secondValue = second.getInt("key",0)); // secondValue is also 15
    
  • Помните, что чем больше объект Preference, тем дольше будут операции get, commit, apply, remove и clear. Поэтому настоятельно рекомендуется разделить ваши данные на разные мелкие объекты.

  • Ваши настройки не будут удалены после обновления приложения. Так что бывают случаи, когда вам нужно создать какую-то схему миграции. Например, у вас есть приложение, которое анализирует локальный JSON при запуске приложения, чтобы сделать это только после первого запуска, вы решили сохранить логический флаг wasLocalDataLoaded. Через некоторое время вы обновили этот JSON и выпустили новую версию приложения. Пользователи будут обновлять свои приложения, но они не будут загружать новый JSON, потому что они уже сделали это в первой версии приложения.

    public class MigrationManager {
     private final static String KEY_PREFERENCES_VERSION = "key_preferences_version";
     private final static int PREFERENCES_VERSION = 2;
    
     public static void migrate(Context context) {
         SharedPreferences preferences = context.getSharedPreferences("pref", Context.MODE_PRIVATE);
         checkPreferences(preferences);
     }
    
     private static void checkPreferences(SharedPreferences thePreferences) {
         final double oldVersion = thePreferences.getInt(KEY_PREFERENCES_VERSION, 1);
    
         if (oldVersion < PREFERENCES_VERSION) {
             final SharedPreferences.Editor edit = thePreferences.edit();
             edit.clear();
             edit.putInt(KEY_PREFERENCES_VERSION, currentVersion);
             edit.commit();
         }
     }
    }
    
  • SharedPreferences хранятся в файле XML в папке данных приложения

    // yours preferences
    /data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PREFS_NAME.xml
    
    // default preferences
    /data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml
    

Руководство по Android.

Образец кода

public class PreferencesManager {

    private static final String PREF_NAME = "com.example.app.PREF_NAME";
    private static final String KEY_VALUE = "com.example.app.KEY_VALUE";

    private static PreferencesManager sInstance;
    private final SharedPreferences mPref;

    private PreferencesManager(Context context) {
        mPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
    }

    public static synchronized void initializeInstance(Context context) {
        if (sInstance == null) {
            sInstance = new PreferencesManager(context);
        }
    }

    public static synchronized PreferencesManager getInstance() {
        if (sInstance == null) {
            throw new IllegalStateException(PreferencesManager.class.getSimpleName() +
                    " is not initialized, call initializeInstance(..) method first.");
        }
        return sInstance;
    }

    public void setValue(long value) {
        mPref.edit()
                .putLong(KEY_VALUE, value)
                .commit();
    }

    public long getValue() {
        return mPref.getLong(KEY_VALUE, 0);
    }

    public void remove(String key) {
        mPref.edit()
                .remove(key)
                .commit();
    }

    public boolean clear() {
        return mPref.edit()
                .clear()
                .commit();
    }
}

Ответ 3

Это мой путь

для записи

SharedPreferences settings = context.getSharedPreferences("prefs", 0);
SharedPreferences.Editor editore = settings.edit();
editore.putString("key", "some value");
editore.apply();

читать

SharedPreferences settings = getSharedPreferences("prefs", 0);
Strings value = settings.getString("key", "");

Ответ 5

Предположим, что в проекте с несколькими разработчиками, работающими над ним, они определяют SharedPreference в Деятельности следующим образом:

SharedPreferences sharedPref = context.getSharedPreferences("prefName", 0);

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

Решение опирается на два варианта, будь то использовать;

  1. SharedPreferences Singleton, использующий строковые ключи.

  2. SharedPreferences Singleton, использующий ключи Enum.

Лично и согласно этой Документации Sharepreference, я предпочитаю использовать ключи Enum, так как это обеспечивает более строгий контроль, когда над проектом работают несколько программистов. У программиста нет другого выбора, кроме как объявить новый ключ в соответствующем классе enum, и все ключи находятся в одном месте.

И чтобы избежать написания стандартного кода, создайте синглтон SharedPreference. Этот одноэлементный класс SharedPreferences помогает централизовать и упростить чтение и запись SharedPreferences в вашем приложении для Android.

Исходный код для двух предоставленных решений можно найти в GitHub

Ответ 6

В kotlin использование SharedPreferences можно упростить следующим образом.

class Prefs(context: Context) {

    companion object {
        private const val PREFS_FILENAME = "app_prefs"

        private const val KEY_MY_STRING = "my_string"
        private const val KEY_MY_BOOLEAN = "my_boolean"
        private const val KEY_MY_ARRAY = "string_array"
    }

    private val sharedPrefs: SharedPreferences =
        context.getSharedPreferences(PREFS_FILENAME, Context.MODE_PRIVATE)

    var myString: String
        get() = sharedPrefs.getString(KEY_MY_STRING, "") ?: ""
        set(value) = sharedPrefs.edit { putString(KEY_MY_STRING, value) }

    var myBoolean: Boolean
        get() = sharedPrefs.getBoolean(KEY_MY_BOOLEAN, false)
        set(value) = sharedPrefs.edit { putBoolean(KEY_MY_BOOLEAN, value) }

    var myStringArray: Array<String>
        get() = sharedPrefs.getStringSet(KEY_MY_ARRAY, emptySet())?.toTypedArray()
            ?: emptyArray()
        set(value) = sharedPrefs.edit { putStringSet(KEY_MY_ARRAY, value.toSet()) }

Здесь sharedPrefs.edit{...} предоставляется библиотекой ktx для ядра android и должен быть реализован путем добавления implementation "androidx.core:core-ktx:1.0.2" зависимостей implementation "androidx.core:core-ktx:1.0.2" на уровне приложения build.gradle.

Вы можете получить экземпляр SharedPreferences с помощью кода:

val prefs = Prefs(context)

Кроме того, вы можете создать объект Prefs Singleton и использовать его в любом месте приложения.

val prefs: Prefs by lazy {
    Prefs(App.instance)
}

где App расширяет Application и должно быть включено в AndroidManifest.xml

App.kt

class App:Application() {
    companion object {
        lateinit var instance: App
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest .....

   <application
        android:name=".App"
        ....

Пример использования:

// get stored value
val myString = prefs.myString

// store value
prefs.myString = "My String Value"

// get stored array
val myStringArray = prefs.myStringArray

// store array
prefs.myStringArray = arrayOf("String 1","String 2","String 3")