Artikel ini menunjukkan contoh membuat delegasi untuk SharedPreferences, yang mengurangi boilerplate dan menjadikan penggunaan SharedPrefernces lebih nyaman. Mereka yang ingin melihat hasilnya dapat pergi ke solusi yang sudah jadi.
Salah satu tugas pengembangan yang mendesak untuk android adalah menyimpan data apa pun di antara sesi. Cara utama untuk melakukan ini: simpan di server atau dalam file di perangkat yang dapat dieksekusi. Salah satu cara pertama yang bisa diketahui oleh pengembang Android pemula mana pun adalah dengan menyimpannya dalam file menggunakan alat SharedPreferences yang sudah jadi.
Katakanlah kita perlu merekam nama pengguna dan kemudian menampilkannya di suatu tempat di aplikasi.
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" } }
Apa itu delegasi dan bagaimana persiapannya
Singkatnya, ini adalah kelas yang merangkum pengaturan dan mendapatkan properti. Siapa yang ingin tahu lebih banyak dokumentasi resmi .
Untuk membuat kelas menjadi delegasi, Anda harus mengimplementasikan antarmuka ReadOnlyProperty untuk val dan ReadWriteProperty untuk var. Kami melewati SharedPreferences, kunci dimana properti dan nilai default akan disimpan melalui konstruktor. Dalam setValue atur nilai untuk mendapatkan Value mendapatkan nilai.
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) } }
Terapkan delegasi
class UserStore(private val preferences: SharedPreferences) { var userName: String by StringPreferencesDelegate(preferences, USER_NAME, "") companion object { private const val USER_NAME = "user_name" } }
Penugasan ke properti delegasi dilakukan oleh kata kunci berdasarkan. Sekarang, setiap kali properti ini diminta atau disetel, metode getValue dan setValue dari delegasi yang dibuat akan diluncurkan.
Hal yang sama sekarang dapat dilakukan dengan bidang lain, misalnya, jika Anda ingin menyimpan telepon pengguna dengan cara yang sama.
var userPhone: String by StringPreferencesDelegate(preferences, USER_PHONE, "")
Generik
Agar tidak membuat delegasi terpisah untuk setiap tipe data, kami menggunakan generalisasi dari dokumentasi resmi generik .
Biasanya kenalan tak sadar pertama dengan generik terjadi ketika turunan dari kelas Daftar dibuat. Untuk itu, ditentukan tipe data spesifik yang digunakannya.
val names :List<String> = listOf("Jon","Bob","Max")
Untuk menentukan tipe data umum untuk kelas setelah namanya, Anda harus menentukan nama variabel ini dalam kurung sudut.
PreferencesDelegate<TValue>(...)
Sekarang Anda perlu menentukan bahwa nilai yang ditetapkan dan nilai default adalah tipe nilai TV.
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) { ... } }
Dengan demikian, sekarang membuat instance kelas terlihat seperti ini:
var userName: String? by StringPreferencesDelegate<String?>(...)
Tetap melakukan pemetaan untuk memperoleh dan mengatur properti, kami menentukan tipe data berdasarkan delaultValue, pada saat yang sama ia memberikan cast cerdas dari nilai ini ke tipe data tertentu, jika terjadi kesalahan dan properti bukan dari tipe TValue, kami mengembalikan 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() } }
Dengan tipe data lain mirip.
Kesalahan khusus
Pertanyaannya tetap apa yang harus dilakukan dengan cabang lain, karena tipe TValue bisa menjadi apa saja.
Ini bentuk yang bagus untuk membuat kesalahan khusus Anda. Jika pengecualian terjadi, maka akan sejelas mungkin apa yang terjadi.
class NotFoundRealizationException(value: Any?) : Exception("not found realization for ${value?.javaClass}") ... else -> throw NotFoundRealizationException(value) ...
Kesimpulan
Secara total, kami mendapatkan delegasi yang siap digunakan:
@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") }
Contoh aplikasi
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" } }