Поле расширения Kotlin для значения SharedPreference по ключу строковой константы
Функции расширения отлично подходят для API SharedPreference в Android. У Джейка Уортона есть интересная реализация во временном коде 32:30 этого видеоурока, где он реализует функцию расширения SharedPreferences следующим образом:
preferences.edit{
set(USER_ID /*some string key constant somewhere*/, 42)
//...
}
в то время как это нормально, его вид многословен.
В этом уроке от Krupal Shah объясняется, как вы можете уменьшить функции расширения методов получения / установки для SharedPreferences до:
preferences[USER_ID] = 42
Log.i("User Id", preferences[USER_ID]) //User Id: 42
Это довольно хорошо, но скобки подразумевают итеративную семантику, IMO. Хотя это и не самое худшее в мире, вы просто хотели бы реализовать расширение поля значения SharedPreferences самой константой ключа.
У меня вопрос, есть ли способ реализовать этот тип расширения на SharedPreferences?
preferences.USER_ID = 42
Log.i("User Id", preferences.USER_ID) //User Id: 42
2 ответа
Во-первых, давайте создадим общий интерфейс для предоставления экземпляра SharedPreferences
:
interface SharedPreferencesProvider {
val sharedPreferences: SharedPreferences
}
После того, как мы должны создать делегат для свойства, которое будет читать / записывать значение в настройки:
object PreferencesDelegates {
fun string(
defaultValue: String = "",
key: String? = null
): ReadWriteProperty<SharedPreferencesProvider, String> =
StringPreferencesProperty(defaultValue, key)
}
private class StringPreferencesProperty(
private val defaultValue: String,
private val key: String?
) : ReadWriteProperty<SharedPreferencesProvider, String> {
override fun getValue(
thisRef: SharedPreferencesProvider,
property: KProperty<*>
): String {
val key = key ?: property.name
return thisRef.sharedPreferences.getString(key, defaultValue)
}
override fun setValue(
thisRef: SharedPreferencesProvider,
property: KProperty<*>,
value: String
) {
val key = key ?: property.name
thisRef.sharedPreferences.save(key, value)
}
}
PreferencesDelegates
Нужно было скрыть реализацию и добавить читабельность к коду. В конце концов это можно использовать так:
class AccountRepository(
override val sharedPreferences: SharedPreferences
) : SharedPreferencesProvider {
var currentUserId by PreferencesDelegates.string()
var currentUserName by string() //With import
var currentUserNickname by string(key = "CUSTOM_KEY", defaultValue = "Unknown")
fun saveUser(id: String, name: String) {
this.currentUserId = id
this.currentUserName = name
}
}
Подобное можно реализовать int
, float
или даже пользовательский тип:
open class CustomPreferencesProperty<T>(
defaultValue: T,
private val key: String?,
private val getMapper: (String) -> T,
private val setMapper: (T) -> String = { it.toString() }
) : ReadWriteProperty<SharedPreferencesProvider, T> {
private val defaultValueRaw: String = setMapper(defaultValue)
override fun getValue(
thisRef: SharedPreferencesProvider,
property: KProperty<*>
): T {
val key = property.name
return getMapper(thisRef.sharedPreferences.getString(key, defaultValueRaw))
}
override fun setValue(
thisRef: SharedPreferencesProvider,
property: KProperty<*>,
value: T
) {
val key = property.name
thisRef.sharedPreferences.save(key, setMapper(value))
}
}
Я написал небольшую библиотеку, которая покрывает такой случай. Вы можете найти остальные реализованные настройки здесь
РЕДАКТИРОВАТЬ. В случае, если вы используете кинжал:
class AccountRepository @Injcet constructor() : SharedPreferencesProvider {
@Inject
override lateinit var sharedPreferences: SharedPreferences
var currentUserId by PreferencesDelegates.string()
...
}
Вы можете определить простое свойство расширения с помощью getter и setter
var SharedPreferences.userId
get() = getInt(USER_ID, 0)
set(value: Int) { edit().putInt(USER_ID, value).apply() }