Комментарии 9
Из проблем:
1. Загрузка данных из файла может происходить в UI потоке при создании инстанса SharedPreferences. В новых версиях SDK это уже вроде как починили.
2. apply() добавляет таску сохранения в очередь, которая общая для всех Preferences. И самое печальное, что пока эта очередь не обработана, активити залипает при закрытии. Для чего это сделано — непонятно. Уж лучше commit() в бэкграунде.
3. Каждый экземпляр SharedPreferences работает со своей копией данных. Надо следить, чтобы на каждый файл был один инстанс.
4. На больших данных работает очень медленно.
3. Каждый экземпляр SharedPreferences работает со своей копией данных. Надо следить, чтобы на каждый файл был один инстанс.
Это не так, в документации чётко прописано:
Interface for accessing and modifying preference data returned by Context#getSharedPreferences. For any particular set of preferences, there is a single instance of this class that all clients share.
4 На больших данных работает очень медленно.
SharedPrefernces сами по себе никогда не были предназначены для хранения больших данных, иначе в них бы были методы такие как: putBundle и putObject
Чтобы не делать для каждого типа данных отдельный делегат воспользуемся обобщениями
Потерять типобезопасность — чего ради? Уж лучше накопипастить правильных классов.
1. preferences.edit() можно создать один раз и хранить ссылку в классе.
2. apply() желательно вызывать отдельно, так как если будет много вызовов setValue(), то на каждый apply() будет отдельное сохранение в файл. Общий Editor может частично решить эту проблему.
3. В preferences для каждого значения сохраняется также и его тип. Можно предусмотреть прозрачную. конвертацию. Иначе, возможны конфликты, например, Integer <-> Long.
1 preferences.edit() можно создать один раз и хранить ссылку в классе.
Хорошая рекомендация. Впринципе тут выбор между скоростью и памятью, поскольку так придется в каждом делегате хранить объект, или же делать его статическим и использовать во всех делегатах, но я не уверен что это будет безопасно со стороны утечек памяти
2 apply() желательно вызывать отдельно, так как если будет много вызовов setValue(), то на каждый apply() будет отдельное сохранение в файл. Общий Editor может частично решить эту проблему.
Тогда нужен дополнительный механизм сохранения, который будет работать внутри класса, что повышает сложность.
По моему опыту в приложении с 30+ экранами таких делегатов штук 10-15, и они вызываются реже чем раз в 5 секунд, так что сильно задумываться о скорости работы не приходилось
interface SharedPreferencesHolder {
val sharedPreferences: SharedPreferences
}
abstract class CommonPreferenceDelegate<T>(val name: String? = null) : ReadWriteProperty<SharedPreferencesHolder, T> {
private fun getPreferenceKey(property: KProperty<*>) = name ?: property.name
final override fun getValue(thisRef: SharedPreferencesHolder, property: KProperty<*>): T {
return getValue(thisRef.sharedPreferences, getPreferenceKey(property))
}
final override fun setValue(thisRef: SharedPreferencesHolder, property: KProperty<*>, value: T) {
setValue(thisRef.sharedPreferences, getPreferenceKey(property), value)
}
abstract fun getValue(prefs: SharedPreferences, key: String): T
abstract fun setValue(prefs: SharedPreferences, key: String, value: T)
}
class BooleanPreferenceDelegate(private val defValue: Boolean, name: String? = null) : CommonPreferenceDelegate<Boolean>(name) {
override fun getValue(prefs: SharedPreferences, key: String) = prefs.getBoolean(key, defValue)
override fun setValue(prefs: SharedPreferences, key: String, value: Boolean) {
prefs.edit().putBoolean(key, value).commit()
}
}
fun SharedPreferencesHolder.boolean(defValue: Boolean = false, name: String? = null) = BooleanPreferenceDelegate(defValue, name)
class Foo(override val sharedPreferences: SharedPreferences) : SharedPreferencesHolder {
val isUserLogged by boolean(false)
}
Ну и нафаршировать сверху классов для остальных типов.
NotFoundRealizationException
Реализация по-бассурмански будет implementation
Да implementation суда подходит лучше.
Считаю плохой идей давать возможность не задавать key ключа, при изменении названия переменной необходимо помнить, что нужно будет указать name
прошлым именем, чтобы не потерять данные.
На мой взгляд получилось не особо проще, но это уже риторический вопрос
Android preferences delegate