1. 项目背景
安卓本地快捷存储方案如 SharedPreferences、MMKV、DataStore 都有明显的缺点,未能兼顾好安全、 性能、类型支持、和用法简易方便的程度,个人基于 DataStore, 做了一个新的方案 KDataStore。
主要有以下优化:
- 单例模式
- 通过委托生成 key。
- 采用 MutbaleStateFlow 即时观察、同步读写、异步写入磁盘。
- 备份数据以处理异常。
在支持 IOS 之后会移到 Multiplatform 分组中。
2. 竞品对比
SharedPreferences | MMKV | DataStore | KDataStore | |
性能 | 启动: 2.5ms | 启动: 2.3ms | 都通过异步,故只测响应: 8.6ms | 启动: 13.5ms |
类型安全 | 否 | 否 | 是 | 是 |
除常见基本类型, String, Set<String> 外的类型支持 | Parcelable | 自定义 | Kt Serializable (包括常见存储类型) | |
读取异常 | 返回空的 HashMap, 即全部采用默认值 | 自行 catch 处理 | 启用备份文件 | |
写入中遇 IOException | 用未写入该数据的备份文件替换,且不再写入该数据。 | 仅 | 记录,下次启动时从备份文件中更新 | |
多进程 | 自行封装 | 支持 | 处于 | 在 |
多平台 | 不支持 | 支持 | ||
加密 | 自行封装 | 支持 | 自行封装 | 需自选加密协议,实现 |
额外优点 | 后台定时异步写入磁盘,ANR前一刻更新的数据不会丢失 | 体积小,jar on Android side 仅 12.6 kb | ||
额外缺点 | 断电或者系统崩溃后容易丢失很多数据 | 比较新 |
以上测试结果采用 30 份 String 数据,机型魅族18s, 源码见 KDataStore.benchmark
。
关于其他地方的存储方案对比分析,绝大多数都有严重错误。官网相对准确,但也很片面。如想探究,建议自己测试并查阅源码。
3. 基础用法
以切换主题的场景为例,使用 KDataStore 存储 Boolean 值代表当前主题
建模
单独分出一个 Android 模块, 常见命名为 settings
。
如果不考虑从 Java 文件中调用,下图中的
@JvmStatic
则是不需要的。
关于 KDSFlow
Android 上的 actual KDSFlow
实现
调用
当我们在 Activity 中使用 KDatStore 时:
- 在 Activity/BasicActivity 中观察Flow/LiveData,绑定主题。
- 选中的 RadioButton 会随用户点击自动变化,根据 isDarkMOde.value 设置起始状态即可,不用绑定。
- 在 RadioGroup 监听中更新存值。
在 Fragment 中观察 Flow 时建议采用 collectOnResume。其配置工作在下文有包括。
再看一下在 Compose 中的使用效果
RadioButton 处更新存值
4. 配置
根目录
配置 build.gradle/build.gradle.kts
如下, 或参考 Github 上的 demo (其中有使用 version catalog)。
plugins{
...
id 'org.jetbrains.kotlin.plugin.serialization' version "$version_kt" apply false
}
模块
plugins {
...
id 'kotlinx-serialization'
}
dependencies {
...
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1'
implementation 'io.github.shawxingkwok:kt-util:1.0.0'
implementation 'io.github.shawxingkwok:kdatastore:1.0.0'
}
调用方
view
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach{
kotlinOptions.freeCompilerArgs += "-Xcontext-receivers"
}
dependencies {
...
implementation 'io.github.shawxingkwok:android-util-view:1.0.0'
implementation 'io.github.shawxingkwok:kdatastore:1.0.0'
implementation project(':本地模型模块名称') // 或远程仓库
}
compose
dependencies{
...
implementation 'io.github.shawxingkwok:kdatastore:1.0.0'
implementation project(':本地模型模块名称') // 或远程仓库
}
如果该调用模块使用了 startup-runtime, 要注意在 dependencies 中包含 KDataStoreInitializer::class.java。
5. 类型支持
kotlinx.serialization
用法类似 Java Serializable, 但多平台,且速度快两倍多。被 Serializable
标记的 class, 基本类型,enum
, Pair
, IntArray
, List
的默认实现等等均可视为 Serializable。
- Non-null 时需声明默认值。
- Nullable 时默认值被限制为 null。
- 自定义时需实现与 Kt Serializable 之间的相互转换。(convert/recover)
6. 迁移
类比下图格式(判断存在 -> 迁移 -> 删除)从其他存储仓库迁移过来。其中的 appContext
源自 KDataStore.
比如取自 SharedPreferences
此外内置 delete, exist 两个函数辅助从 KDataStore 迁移到别处。
警告以防止误用,并无异常风险。
7. 可选参数
加密部分需从 Java 标准库或其他库中自选加密协议,实现 cipher。
加密会将启动时间提升一倍左右。Android 在 api 29 版本引入了沙盒机制,实现了数据隔离,脱离加密也相对安全。
8. 重置
全部重置代码如下
部分重置代码如下
Settings.isDarkMode.reset()
标签:github,持久,implementation,写入,kotlinx,易用,ms,KDataStore
From: https://blog.51cto.com/u_64214/7131911