前言
在 Kotlin Multiplatform 开发中,跨平台数据存储是一个基础且常见的需求。本文主要介绍如何使用 multiplatform-settings 库来统一处理各平台的数据存储逻辑。由于存储层通常与业务逻辑解耦,开发者也可以根据实际情况,通过 expect 和 actual 机制自行实现定制化方案。
实现
引包
首先,在项目的共享模块中添加 multiplatform-settings 依赖:
implementation("com.russhwolf:multiplatform-settings:1.2.0")
实例创建
引入依赖后,需要针对不同平台创建相应的 Settings 实例。首先定义一个 expect 类:
expect class SettingsWrapper {
fun createSettings(): Settings
}
对于 Desktop/Jvm 平台,实际实现如下:
actual class SettingsWrapper {
actual fun createSettings(): Settings {
val delegate: Preferences = Preferences.userRoot()
return PreferencesSettings(delegate)
}
}
对于 Android 平台,默认实现基于 SharedPreferences:
actual class SettingsWrapper(private val context: Context) {
actual fun createSettings(): Settings {
val delegate = context.getSharedPreferences("your_name", Context.MODE_PRIVATE)
return SharedPreferencesSettings(delegate)
}
}
需要注意的是,Android 平台更推荐使用 DataStore 替代 SharedPreferences,因为 DataStore 支持异步操作和流式 API,能避免主线程阻塞并提供更好的数据一致性。若希望使用 DataStore,需额外引入以下依赖:
implementation("com.russhwolf:multiplatform-settings-datastore:1.2.0")
implementation("com.russhwolf:multiplatform-settings-coroutines:1.2.0")
然后,使用 FlowSettings 接口进行抽象:
expect class SettingsWrapper {
@OptIn(ExperimentalSettingsApi::class)
fun createSettings(): FlowSettings
}
Desktop/Jvm 平台实现:
actual class SettingsWrapper {
@OptIn(ExperimentalSettingsApi::class)
actual fun createSettings(): FlowSettings {
val delegate: Preferences = Preferences.userRoot()
return PreferencesSettings(delegate).toFlowSettings()
}
}
Android 平台实现:
actual class SettingsWrapper(private val context: Context) {
companion object {
private val Context.dataStore by preferencesDataStore("your_name")
}
@OptIn(ExperimentalSettingsImplementation::class, ExperimentalSettingsApi::class)
actual fun createSettings(): FlowSettings {
return DataStoreSettings(context.dataStore)
}
}
依赖注入
为了便于管理,建议使用 Koin 进行依赖注入。对于 Desktop/Jvm 平台,模块配置如下:
single { SettingsWrapper().createSettings() }
对于 Android 平台:
single {
SettingsWrapper(
context = MainActivity.mainContext!!
).createSettings()
}
在公共代码中,即可通过注入的 Settings 实例进行数据操作。对于普通 Settings,可以按以下方式封装:
class DataStorageManager(private val settings: Settings) {
private val observableSettings: ObservableSettings by lazy { settings as ObservableSettings }
}
对于 FlowSettings,则需使用协程支持:
class DataStorageManager @OptIn(ExperimentalSettingsApi::class) constructor(private val settings: FlowSettings) {
@OptIn(ExperimentalSettingsApi::class)
private val observableSettings: ObservableSettings by lazy { settings.toBlockingObservableSettings() }
}
使用
存储数据
存储字符串数据的函数示例:
fun setString(key: String, value: String) {
observableSettings.set(key = key, value = value)
}
读取数据
若希望使用流式读取,可调用 getStringFlow:
@OptIn(ExperimentalSettingsApi::class)
fun getFlowString(key: String, defaultValue: String) = settings.getStringFlow(key, defaultValue)
若只需同步读取,则使用:
fun getNonFlowString(key: String, defaultValue: String) = observableSettings.getString(
key = key,
defaultValue = defaultValue,
)
源码参考
完整实现可参考项目 Tomoyo。