Android首选项委托

本文展示了一个为SharedPreferences创建委托的示例,该示例减少了样板,使使用SharedPrefernces更方便。 那些希望看到结果的人可以使用现成的解决方案。


android开发的紧迫任务之一是保存会话之间的任何数据。 这样做的主要方法:存储在服务器上或可执行设备上的文件中。 任何新手Android开发人员都可以了解这一点的最早方法之一就是使用现成的SharedPreferences工具将其存储在文件中。


假设我们需要记录用户名,然后将其显示在应用程序中的某个位置。


class UserStore(private val preferences: SharedPreferences) { fun getUserName(): String? { return preferences.getString(USER_NAME, "") } fun saveUserName(userName: String) { preferences.edit().putString(USER_NAME, userName).apply() } companion object { private const val USER_NAME = "user_name" } } 

什么是代表及其准备方式


简而言之,这是一个封装设置和获取属性的类。 谁想知道更多官方文档


要使一个类成为委托,您必须为val实现ReadOnlyProperty接口,为var实现ReadWriteProperty。 我们传递SharedPreferences,这是用于通过构造函数存储属性和默认值的键。 在setValue中,将值设置为getValue获取值。


 class StringPreferencesDelegate( private val preferences: SharedPreferences, private val name: String, private val defValue: String ) : ReadWriteProperty<Any?, String?> { override fun setValue(thisRef: Any?, property: KProperty<*>, value: String?) { preferences.edit().putString(name, value).apply() } override fun getValue(thisRef: Any?, property: KProperty<*>): String? { return preferences.getString(name, defValue) } } 

申请代表


 class UserStore(private val preferences: SharedPreferences) { var userName: String by StringPreferencesDelegate(preferences, USER_NAME, "") companion object { private const val USER_NAME = "user_name" } } 

委托属性的分配由by关键字完成。 现在,每次请求或设置此属性时,都会启动创建的委托的getValue和setValue方法。


例如,如果您想以相同的方式保存用户的电话,现在可以对其他字段执行相同的操作。


  var userPhone: String by StringPreferencesDelegate(preferences, USER_PHONE, "") 

泛型


为了不为每种数据类型创建单独的委托,我们使用通用官方文档的概括。


通常,在创建List类的实例时,会先无意识地熟悉泛型。 为此,确定其工作的特定数据类型。


 val names :List<String> = listOf("Jon","Bob","Max") 

要在类名称后指定通用数据类型,必须在尖括号中指定此变量的名称。


 PreferencesDelegate<TValue>(...) 

现在,您需要指定设置值和默认值的类型为TValue。


 class PreferencesDelegate<TValue>( val preferences: SharedPreferences, private val name: String, private val defValue: TValue ) : ReadWriteProperty<Any?, TValue> { override fun getValue(thisRef: Any?, property: KProperty<*>): TValue { ... } override fun setValue(thisRef: Any?, property: KProperty<*>, value: TValue) { ... } } 

因此,现在创建类的实例如下所示:


 var userName: String? by StringPreferencesDelegate<String?>(...) 

剩下要做的是获取和设置属性的映射,我们通过delaultValue确定数据类型,同时将该值智能转换为特定数据类型,如果出现问题并且属性不是TValue类型,则返回defValue。


 override fun getValue(thisRef: Any?, property: KProperty<*>): TValue { with(preferences) { return when (defValue) { is Boolean -> (preferences.getBoolean(name, defValue) as? TValue) ?: defValue ... } } } override fun setValue(thisRef: Any?, property: KProperty<*>, value: TValue) { with(preferences.edit()) { when (value) { is Boolean -> putBoolean(name, value) ... } apply() } } 

与其他数据类型相似。


自定义错误


问题仍然是如何处理else分支,因为TValue类型可以绝对是任何东西。
这是犯您的自定义错误的好方法。 如果发生异常,那么将尽可能清楚地了解发生了什么。


  class NotFoundRealizationException(value: Any?) : Exception("not found realization for ${value?.javaClass}") ... else -> throw NotFoundRealizationException(value) ... 

结论


总计,我们有一个可供使用的代表:


 @Suppress("UNCHECKED_CAST") class PreferencesDelegate<TValue>( val preferences: SharedPreferences, private val name: String, private val defValue: TValue ) : ReadWriteProperty<Any?, TValue> { override fun getValue(thisRef: Any?, property: KProperty<*>): TValue { with(preferences) { return when (defValue) { is Boolean -> (getBoolean(name, defValue) as? TValue) ?: defValue is Int -> (getInt(name, defValue) as TValue) ?: defValue is Float -> (getFloat(name, defValue) as TValue) ?: defValue is Long -> (getLong(name, defValue) as TValue) ?: defValue is String -> (getString(name, defValue) as TValue) ?: defValue else -> throw NotFoundRealizationException(defValue) } } } override fun setValue(thisRef: Any?, property: KProperty<*>, value: TValue) { with(preferences.edit()) { when (value) { is Boolean -> putBoolean(name, value) is Int -> putInt(name, value) is Float -> putFloat(name, value) is Long -> putLong(name, value) is String -> putString(name, value) else -> throw NotFoundRealizationException(value) } apply() } } class NotFoundRealizationException(defValue: Any?) : Exception("not found realization for $defValue") } 

应用实例


 class UserStore(private val preferences: SharedPreferences) { var userName: String by PreferencesDelegate(preferences, USER_NAME, "") var userPhone: String by PreferencesDelegate(preferences, USER_PHONE, "") var isShowLicence: Boolean by PreferencesDelegate(preferences, USER_LICENCE, false) companion object { private const val USER_NAME = "user_name" private const val USER_PHONE = "user_phone" private const val USER_LICENCE = "user_licence" } } 

Source: https://habr.com/ru/post/zh-CN461161/


All Articles