Délégué aux préférences Android

Cet article montre un exemple de création d'un délégué pour SharedPreferences, ce qui réduit le passe-partout et rend l'utilisation de SharedPrefernces plus pratique. Ceux qui veulent voir le résultat peuvent aller vers une solution toute faite.


L'une des tĂąches urgentes de dĂ©veloppement pour Android consiste Ă  enregistrer toutes les donnĂ©es entre les sessions. Les principales façons de le faire: stocker sur le serveur ou dans des fichiers sur le pĂ©riphĂ©rique exĂ©cutable. L'un des tout premiers moyens que tout dĂ©veloppeur Android dĂ©butant peut connaĂźtre est de le stocker dans un fichier Ă  l'aide de l'outil SharedPreferences prĂȘt Ă  l'emploi.


Disons que nous devons enregistrer le nom d'utilisateur, puis l'afficher quelque part dans l'application.


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'est-ce qu'un délégué et comment est-il préparé


En bref, il s'agit d'une classe qui encapsule la définition et l'obtention d'une propriété. Qui veut en savoir plus de documentation officielle .


Pour faire d'une classe un délégué, vous devez implémenter l'interface ReadOnlyProperty pour val et ReadWriteProperty pour var. Nous transmettons SharedPreferences, la clé par laquelle la propriété et la valeur par défaut seront stockées via le constructeur. Dans setValue, définissez la valeur sur getValue pour obtenir la valeur.


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

Appliquer délégué


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

L'affectation à une propriété de délégué se fait par le mot-clé by. Désormais, chaque fois que cette propriété est demandée ou définie, les méthodes getValue et setValue du délégué créé seront lancées.


La mĂȘme chose peut maintenant ĂȘtre effectuĂ©e avec d'autres champs, par exemple, si vous souhaitez enregistrer le tĂ©lĂ©phone de l'utilisateur de la mĂȘme maniĂšre.


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

Générique


Afin de ne pas faire de délégué séparé pour chaque type de données, nous utilisons des généralisations de la documentation officielle générique .


Habituellement, la premiÚre connaissance inconsciente du générique se produit lorsqu'une instance de la classe List est créée. Pour cela, le type de données spécifique avec lequel il travaille est déterminé.


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

Pour spécifier un type de données généralisé pour une classe aprÚs son nom, vous devez spécifier le nom de cette variable entre crochets.


 PreferencesDelegate<TValue>(...) 

Vous devez maintenant spécifier que la valeur définie et la valeur par défaut sont de type 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) { ... } } 

Par conséquent, la création d'une instance de la classe ressemble maintenant à ceci:


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

Il reste Ă  faire le mappage de l'obtention et de la dĂ©finition des propriĂ©tĂ©s, nous dĂ©terminons le type de donnĂ©es par delaultValue, en mĂȘme temps qu'il donne une distribution intelligente de cette valeur au type de donnĂ©es spĂ©cifique, si quelque chose s'est mal passĂ© et que la propriĂ©tĂ© n'est pas de type TValue, nous renvoyons 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() } } 

Avec d'autres types de données, c'est similaire.


Erreur personnalisée


La question reste de savoir quoi faire avec la branche else, car le type TValue peut ĂȘtre absolument n'importe quoi.
C'est une bonne forme de faire votre erreur personnalisée. Si une exception se produit, il sera alors aussi clair que possible ce qui s'est passé.


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

Conclusion


Au total, nous prĂ©parons un dĂ©lĂ©guĂ© prĂȘt Ă  l'emploi:


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

Exemple d'application


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


All Articles