Delegado de preferencias de Android

Este art铆culo muestra un ejemplo de c贸mo crear un delegado para SharedPreferences, lo que reduce la repetitiva y hace que el uso de SharedPrefernces sea m谩s conveniente. Aquellos que quieran ver el resultado pueden recurrir a una soluci贸n preparada.


Una de las tareas urgentes de desarrollo para Android es guardar los datos entre sesiones. Las principales formas de hacer esto: almacenar en el servidor o en archivos en el dispositivo ejecutable. Una de las primeras formas en que cualquier desarrollador novato de Android puede conocer esto es almacenarlo en un archivo utilizando la herramienta SharedPreferences ya preparada.


Digamos que necesitamos registrar el nombre de usuario y luego mostrarlo en alg煤n lugar de la aplicaci贸n.


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" } } 

驴Qu茅 es un delegado y c贸mo se prepara?


En pocas palabras, esta es una clase que encapsula la configuraci贸n y la obtenci贸n de una propiedad. 驴Qui茅n quiere saber m谩s documentaci贸n oficial ?


Para hacer que una clase sea un delegado, debe implementar la interfaz ReadOnlyProperty para val y ReadWriteProperty para var. Pasamos SharedPreferences, la clave por la cual la propiedad y el valor predeterminado se almacenar谩n a trav茅s del constructor. En setValue establece el valor para getValue obtener el valor.


 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) } } 

Aplicar delegado


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

La asignaci贸n a una propiedad delegada se realiza mediante la palabra clave. Ahora, cada vez que se solicita o establece esta propiedad, se iniciar谩n los m茅todos getValue y setValue del delegado creado.


Ahora se puede hacer lo mismo con otros campos, por ejemplo, si desea guardar el tel茅fono del usuario de la misma manera.


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

Gen茅rico


Para no hacer un delegado separado para cada tipo de datos, utilizamos generalizaciones de la documentaci贸n oficial gen茅rica .


Por lo general, el primer conocimiento inconsciente de gen茅rico ocurre cuando se crea una instancia de la clase Lista. Para ello, se determina el tipo de datos espec铆fico con el que trabaja.


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

Para especificar un tipo de datos generalizado para una clase despu茅s de su nombre, debe especificar el nombre de esta variable entre par茅ntesis angulares.


 PreferencesDelegate<TValue>(...) 

Ahora debe especificar que el valor establecido y el valor predeterminado son del tipo 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) { ... } } 

En consecuencia, ahora crear una instancia de la clase se ve as铆:


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

Queda por hacer el mapeo de obtenci贸n y configuraci贸n de las propiedades, determinamos el tipo de datos por delaultValue, al mismo tiempo da un reparto inteligente de este valor al tipo de datos espec铆fico, si algo sali贸 mal y la propiedad no es del tipo TValue, devolvemos 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() } } 

Con otros tipos de datos es similar.


Error personalizado


La pregunta sigue siendo qu茅 hacer con la rama else, ya que el tipo TValue puede ser absolutamente cualquier cosa.
Es una buena forma cometer su error personalizado. Si ocurre una excepci贸n, entonces ser谩 lo m谩s claro posible lo que sucedi贸.


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

Conclusi贸n


En total, tenemos un delegado listo para usar:


 @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") } 

Ejemplo de aplicaci贸n


 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/461161/


All Articles