Android自定义PreferenceDataStore的getString()方法实现问题
我在Android应用中使用继承自PreferenceDataStore的自定义DataStore存储设置页面偏好,需要实现getString()方法,因为PreferenceDataStore的默认getString()方法仅返回默认值,无实际逻辑:
/** * Retrieve a String value from the data store. * * @param key The name of the preference to retrieve. * @param defValue Value to return if this preference does not exist. * @see #putString(String, String) */ @Nullable default String getString(String key, @Nullable String defValue) { return defValue; }
我的SettingsPreferences类代码如下:
class SettingsPreferences(lifecycleCoroutineScope: LifecycleCoroutineScope, context: Context) : PreferenceDataStore() { companion object { private const val TAG = "SettingsPreferences" val THEME = stringPreferencesKey("theme") } private val scope = lifecycleCoroutineScope private val currentContext = context override fun putString(key: String?, value: String?) { scope.launch { currentContext.dataStore.edit { it[stringPreferencesKey(key.toString())] = value ?: "" } } } override fun getString(key: String?, defValue: String?): String? { //TODO implement getString return super.getString(key, defValue) } fun storeTheme(theme: String) { putString(THEME.name, theme) } }
我尝试用currentContext.dataStore.data.first()[stringPreferencesKey(key.toString())]获取值,但first()是异步方法,需要将getString()设为挂起函数或使用协程,然而继承的getString()无法设置为挂起函数,使用协程也无法直接返回值,请问该如何实现这个方法?
注:虽有自定义实现更新设置页面值的替代方案,但较为繁琐,我的目标是实现getString()方法,让PreferenceFragmentCompat类能正确处理。
由于PreferenceDataStore的getString()是同步方法,而DataStore的读取是异步操作,这里可以通过阻塞线程等待协程结果的方式实现,同时结合内存缓存优化性能,避免重复IO操作:
基础实现(直接阻塞读取)
修改getString()方法,使用runBlocking在IO线程阻塞获取DataStore数据:
override fun getString(key: String?, defValue: String?): String? { key ?: return defValue return try { runBlocking(Dispatchers.IO) { currentContext.dataStore.data.first()[stringPreferencesKey(key)] ?: defValue } } catch (e: Exception) { Log.e(TAG, "Failed to get string value for key: $key", e) defValue } }
优化实现(添加内存缓存)
为了减少DataStore的重复读取操作,维护一个内存缓存同步数据变化,提升响应速度:
class SettingsPreferences(lifecycleCoroutineScope: LifecycleCoroutineScope, context: Context) : PreferenceDataStore() { companion object { private const val TAG = "SettingsPreferences" val THEME = stringPreferencesKey("theme") } private val scope = lifecycleCoroutineScope private val currentContext = context // 内存缓存,用于快速同步读取 private val cache = mutableMapOf<String, String?>() init { // 监听DataStore数据变化,实时更新缓存 scope.launch(Dispatchers.IO) { currentContext.dataStore.data.collect { preferences -> preferences.asMap().forEach { (key, value) -> if (value is String) { cache[key] = value } } } } } override fun putString(key: String?, value: String?) { key ?: return // 先更新缓存,保证读取一致性 cache[key] = value scope.launch(Dispatchers.IO) { currentContext.dataStore.edit { it[stringPreferencesKey(key)] = value ?: "" } } } override fun getString(key: String?, defValue: String?): String? { key ?: return defValue // 优先从缓存读取,缓存未命中再从DataStore获取并更新缓存 return cache[key] ?: runBlocking(Dispatchers.IO) { val value = currentContext.dataStore.data.first()[stringPreferencesKey(key)] ?: defValue cache[key] = value value } } fun storeTheme(theme: String) { putString(THEME.name, theme) } }
这样既满足了getString()的同步返回要求,又通过缓存减少了IO开销,同时保证了数据与DataStore的一致性。
内容的提问来源于stack exchange,提问作者Goutham

