本地存储(DataStore / KV)
相关依赖
- MMKV:https://github.com/Tencent/MMKV
- kotlinx-serialization JSON:https://github.com/Kotlin/kotlinx.serialization
- 版本配置:
gradle/libs.versions.toml中的mmkv、kotlinx-serialization-json
app/src/main/java/com/joker/kit/core/datastore 封装了轻量存储能力,当前通过 MMKVUtils + kotlinx.serialization 实现,负责向 core/data/repository 提供可注入的数据源。目录结构与数据库模块类似:DataSource 接口 -> 实现 -> Hilt 模块 -> Repository。
目录结构
| 目录/文件 | 说明 |
|---|---|
datasource/auth/ | 认证信息本地数据源接口与实现。 |
datasource/userinfo/ | 用户信息本地数据源接口与实现。 |
di/DataStoreModule.kt | Hilt 模块,绑定 DataSource 接口与实现。 |
认证数据源:AuthStoreDataSource
- 接口:提供
saveAuth、getAuth、getToken、clearAuth、isLoggedIn等方法。 - 实现:
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,提供“保存 / 清除 / 重新读取”按钮,并用两个输入框写入 nickName、avatar(或 ID)。这样即可看到仓库层如何封装 DataSource,再由 ViewModel 直接调用。
扩展示例:SettingStore
若想缓存 App 设置,可按以下步骤扩展:
- 在
app/src/main/java/com/joker/kit/core/datastore/datasource/setting下新增接口:kotlininterface SettingStoreDataSource { suspend fun saveTheme(theme: String) suspend fun getTheme(): String } - 编写实现类
SettingStoreDataSourceImpl,使用MMKVUtils.putString/getString读写值。 - 在
DataStoreModule中@Binds新接口与实现:kotlin@Binds @Singleton abstract fun bindSettingStoreDataSource( impl: SettingStoreDataSourceImpl ): SettingStoreDataSource - 在
core/data/repository/SettingRepository中注入SettingStoreDataSource,并对外暴露语义化方法:kotlinclass SettingRepository @Inject constructor( private val settingStore: SettingStoreDataSource ) { suspend fun updateTheme(theme: String) = settingStore.saveTheme(theme) suspend fun currentTheme(): String = settingStore.getTheme() }
扩展指南
- 新增 DataSource 接口与实现:按 feature 归类目录,并处理序列化/容错逻辑。
- 在
DataStoreModule绑定:通过@Binds将实现注入 Hilt 容器。 - 在仓库中组合:让 Repository 注入新的 DataSource,与网络/数据库数据源一起决定读写策略。
- 保持约束:统一使用
MMKVUtils或其他 KV 库;若需要同步多端或事务能力,考虑迁移到数据库模块。
通过这种方式,项目中的所有本地 KV/轻量配置都能通过仓库层统一对外暴露,既方便测试又利于维护。