Kotlin Multiplatform 数据存储实现方案详解

Viewed 0

前言

在 Kotlin Multiplatform 开发中,跨平台数据存储是一个基础且常见的需求。本文主要介绍如何使用 multiplatform-settings 库来统一处理各平台的数据存储逻辑。由于存储层通常与业务逻辑解耦,开发者也可以根据实际情况,通过 expectactual 机制自行实现定制化方案。

实现

引包

首先,在项目的共享模块中添加 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

参考资料

0 Answers