SharedPreference 值的 Kotlin 扩展字段(按字符串常量键)
Kotlin extension field for SharedPreference value by string constant key
扩展函数非常适合 android 中的 SharedPreference api。 Jake Wharton 在 this 视频教程的时间代码 32:30 有一个有趣的实现,他在其中实现了 SharedPreferences 扩展功能,如下所示:
preferences.edit{
set(USER_ID /*some string key constant somewhere*/, 42)
//...
}
虽然还可以,但有点冗长。
This Krupal Shah 的教程解释了如何将 SharedPreferences 的 getter/setter 扩展函数减少到:
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
首先,让我们创建通用接口来提供 SharedPreferences
的实例:
interface SharedPreferencesProvider {
val sharedPreferences: SharedPreferences
}
在我们必须为 属性 创建委托之后,它将 read/write 值设置为首选项:
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))
}
}
我编写了涵盖这种情况的小型库。您可以找到其余已实施的首选项 here
编辑。如果您使用的是匕首:
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() }
扩展函数非常适合 android 中的 SharedPreference api。 Jake Wharton 在 this 视频教程的时间代码 32:30 有一个有趣的实现,他在其中实现了 SharedPreferences 扩展功能,如下所示:
preferences.edit{
set(USER_ID /*some string key constant somewhere*/, 42)
//...
}
虽然还可以,但有点冗长。
This Krupal Shah 的教程解释了如何将 SharedPreferences 的 getter/setter 扩展函数减少到:
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
首先,让我们创建通用接口来提供 SharedPreferences
的实例:
interface SharedPreferencesProvider {
val sharedPreferences: SharedPreferences
}
在我们必须为 属性 创建委托之后,它将 read/write 值设置为首选项:
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))
}
}
我编写了涵盖这种情况的小型库。您可以找到其余已实施的首选项 here
编辑。如果您使用的是匕首:
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() }