Skip to content

本地存储(DataStore / KV)

相关依赖

app/src/main/java/com/joker/kit/core/datastore 封装了轻量存储能力,当前通过 MMKVUtils + kotlinx.serialization 实现,负责向 core/data/repository 提供可注入的数据源。目录结构与数据库模块类似:DataSource 接口 -> 实现 -> Hilt 模块 -> Repository

目录结构

目录/文件说明
datasource/auth/认证信息本地数据源接口与实现。
datasource/userinfo/用户信息本地数据源接口与实现。
di/DataStoreModule.ktHilt 模块,绑定 DataSource 接口与实现。

认证数据源:AuthStoreDataSource

  • 接口:提供 saveAuthgetAuthgetTokenclearAuthisLoggedIn 等方法。
  • 实现AuthStoreDataSourceImpl 使用 MMKVUtils 存储 JSON 文本,并通过 kotlinx.serialization 进行序列化/反序列化,所有操作都在 KEY_AUTH 下管理。
  • 仓库AuthStoreRepository 注入该 DataSource,为上层提供更语义化的 API(例如 shouldRefreshToken())。

用户信息数据源:UserInfoStoreDataSource

该接口负责用户资料缓存,包含:

方法说明
saveUserInfo(User) / getUserInfo()保存或读取完整用户信息。
updateUserInfo(Map<String, Any?>)局部更新某些字段,内部会解析 JSON 并覆盖指定字段。
clearUserInfo()清除缓存。
getUserId() / getNickName() / getAvatarUrl()便捷读取常用字段。

实现类 UserInfoStoreDataSourceImpl 同样基于 MMKVUtils,并使用 JsonObject 对局部更新做容错处理。

与数据层的关系

  • core/data/repository/AuthStoreRepository 注入 AuthStoreDataSource,对外提供 token/登录状态管理。
  • core/data/repository/UserInfoStoreRepository 注入 UserInfoStoreDataSource,负责用户资料缓存。
  • ViewModel 或 UseCase 永远只依赖仓库,底层 DataSource 则专注于存储细节(key、序列化、异常处理)。

示例:用户信息本地存储页面

feature/demo/view/LocalStorageScreen.kt + feature/demo/viewmodel/LocalStorageViewModel.kt 演示了只依赖仓库的最小实现:

kotlin
@HiltViewModel
class LocalStorageViewModel @Inject constructor(
    navigator: AppNavigator,
    userState: UserState,
    private val userInfoStoreRepository: UserInfoStoreRepository
) : BaseViewModel(navigator, userState) {
    // 输入状态
    val userId = MutableStateFlow("1")
    val nickName = MutableStateFlow("")
    val avatar = MutableStateFlow("")
    val user: StateFlow<User?> = MutableStateFlow<User?>(null).asStateFlow()

    fun saveUser() = viewModelScope.launch {
        val user = User(id = userId.value.toLongOrNull() ?: 0, nickName = nickName.value, avatarUrl = avatar.value)
        userInfoStoreRepository.saveUserInfo(user)
        (user as MutableStateFlow).value = user
    }

    fun clearUser() = viewModelScope.launch {
        userInfoStoreRepository.clearUserInfo()
        (user as MutableStateFlow).value = null
    }

    fun loadUser() = viewModelScope.launch {
        (user as MutableStateFlow).value = userInfoStoreRepository.getUserInfo()
    }
}

界面通过 collectAsState() 订阅 user,提供“保存 / 清除 / 重新读取”按钮,并用两个输入框写入 nickNameavatar(或 ID)。这样即可看到仓库层如何封装 DataSource,再由 ViewModel 直接调用。

扩展示例:SettingStore

若想缓存 App 设置,可按以下步骤扩展:

  1. app/src/main/java/com/joker/kit/core/datastore/datasource/setting 下新增接口:
    kotlin
    interface SettingStoreDataSource {
        suspend fun saveTheme(theme: String)
        suspend fun getTheme(): String
    }
  2. 编写实现类 SettingStoreDataSourceImpl,使用 MMKVUtils.putString/getString 读写值。
  3. DataStoreModule@Binds 新接口与实现:
    kotlin
    @Binds
    @Singleton
    abstract fun bindSettingStoreDataSource(
        impl: SettingStoreDataSourceImpl
    ): SettingStoreDataSource
  4. core/data/repository/SettingRepository 中注入 SettingStoreDataSource,并对外暴露语义化方法:
    kotlin
    class SettingRepository @Inject constructor(
        private val settingStore: SettingStoreDataSource
    ) {
        suspend fun updateTheme(theme: String) = settingStore.saveTheme(theme)
        suspend fun currentTheme(): String = settingStore.getTheme()
    }

扩展指南

  1. 新增 DataSource 接口与实现:按 feature 归类目录,并处理序列化/容错逻辑。
  2. DataStoreModule 绑定:通过 @Binds 将实现注入 Hilt 容器。
  3. 在仓库中组合:让 Repository 注入新的 DataSource,与网络/数据库数据源一起决定读写策略。
  4. 保持约束:统一使用 MMKVUtils 或其他 KV 库;若需要同步多端或事务能力,考虑迁移到数据库模块。

通过这种方式,项目中的所有本地 KV/轻量配置都能通过仓库层统一对外暴露,既方便测试又利于维护。