微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Androidx 首选项库与 DataStore 首选项

如何解决Androidx 首选项库与 DataStore 首选项

我之前按照 Google 在文档中的建议,用新的 DataStore 替换了我应用中的 SharedPreferences,以获得一些明显的好处。然后是添加设置屏幕的时候了,我找到了首选项库。当我看到库认使用 SharedPreferences 而没有切换到 DataStore 的选项时,困惑就来了。您可以使用 setPreferenceDataStore 提供自定义存储实现,但 DataStore 不实现 PreferenceDataStore 接口,这由开发人员决定。是的,这个命名也非常令人困惑。当我发现没有关于将 DataStore 与 Preferences Library 一起使用的文章或问题时,我变得更加困惑,所以我觉得我错过了一些东西。人们是否同时使用这两种存储解决方案?还是其中之一?如果我要在 DataStore 中实现 PreferenceDataStore,有什么我应该注意的问题/陷阱吗?

解决方法

对于阅读问题并思考 setPreferenceDataStore 解决方案的任何人。使用 DataStore 而不是 SharedPreferences 实现您自己的 PreferencesDataStore 一目了然。

class SettingsDataStore(private val dataStore: DataStore<Preferences>): PreferenceDataStore() {

    override fun putString(key: String,value: String?) {
        CoroutineScope(Dispatchers.IO).launch {
            dataStore.edit {  it[stringPreferencesKey(key)] = value!! }
        }
    }

    override fun getString(key: String,defValue: String?): String {
        return runBlocking { dataStore.data.map { it[stringPreferencesKey(key)] ?: defValue!! }.first() }
    }

    ...
}

然后在您的片段中设置数据存储

@AndroidEntryPoint
class AppSettingsFragment : PreferenceFragmentCompat() {

    @Inject
    lateinit var dataStore: DataStore<Preferences>

    override fun onCreatePreferences(savedInstanceState: Bundle?,rootKey: String?) {
        preferenceManager.preferenceDataStore = SettingsDataStore(dataStore)
        setPreferencesFromResource(R.xml.app_preferences,rootKey)
    }
}

但是此解决方案存在一些问题。根据 documentation runBlockingfirst() 同步读取值是首选方式,但应谨慎使用。

确保在调用 preferenceDataStore 之前设置 setPreferencesFromResource 以避免加载问题,其中默认实现 (sharedPreferences) 将用于初始加载。

几周前,在我最初尝试实现 PreferenceDataStore 时,我遇到了 long 类型键的问题。我的设置屏幕正确显示并保存了 EditTextPreference 的数值,但流没有为这些键发出任何值。 EditTextPreference 将数字保存为字符串可能存在问题,因为在 xml 中设置 inputType 似乎没有效果(至少在输入键盘上没有效果)。虽然将数字保存为字符串可能有效,但这也需要将数字作为字符串读取。因此,您失去了原始类型的类型安全性。

也许对设置和数据存储库进行一两次更新,可能会有针对这种情况的官方工作解决方案。

,

我在使用 DataStore 时遇到了同样的问题。不仅 DataStore 没有实现 PreferenceDataStore,而且我认为编写一个适配器来桥接两者是不可能的,因为 DataStore 使用 Kotlin Flow 并且是异步的,而 PreferenceDataStore 假设 get 和 put 操作是同步的。

我对此的解决方案是使用回收器视图手动编写首选项屏幕。幸运的是,ConcatAdapter 使它变得更容易,因为我基本上可以为每个首选项创建一个适配器,然后使用 ConcatAdapter 将它们组合成一个适配器。

我最终得到的是一个 PreferenceItemAdapter,它具有模仿偏好行为的可变 titlesummaryvisibleenabled 属性库,以及受 Jetpack Compose 启发的 API,如下所示:

preferenceGroup {
  preference {
    title("Name")
    summary(datastore.data.map { it.name })
    onClick {
      showDialog {
        val text = editText(datastore.data.first().name)
        negativeButton()
        positiveButton()
          .onEach { dataStore.edit { settings -> settings.name = text.first } }.launchIn(lifecycleScope)
      }
    }
  }
  preference {
    title("Date")
    summary(datastore.data.map { it.parsedDate?.format(dateFormatter) ?: "Not configured" })
    onClick {
      showDatePickerDialog(datastore.data.first().parsedDate ?: LocalDate.now()) { newDate ->
        lifecycleScope.launch { dataStore.edit { settings -> settings.date = newDate } }
      }
    }
  }
}

在这种方法中有更多的手动代码,但我发现它比尝试根据我的意愿改变首选项库更容易,并且为我的项目提供了所需的灵活性(它还在 Firebase 中存储了一些首选项)。

,

我会添加我自己的策略来解决不兼容问题,以防它对某些人有用:

我坚持使用首选项库并将 android:persistent="false" 添加到我所有的可编辑首选项中,因此他们根本不会使用 SharedPreferences。然后我可以自由地保存和加载偏好值。通过单击/更改侦听器 → 视图模型 → 存储库存储它们,并通过观察者将它们反射回来。

绝对比一个好的自定义解决方案更混乱,但它对我的小应用程序运行良好。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。