前言
目前Android简单数据存储经常使用如下三个工具:
- SharedPreferences
- MMKV
- DataStore
众所周知:SharedPreferences会引发ANR,所以Google官方推出了DataStore,但是国内使用MMKV居多,因为MMKV具有以下优点:
- 简单傻瓜式使用
- 快速内存映射快速读写(MMKV本质上实现了SharedPreferences,其底层实现使用MMAP)
但是我更加推荐使用DataStore,原因如下:
- Google官方组件
- 支持Kotlin协程和Flow
- 支持跨进程
Preferences DataStore
Preferences DataStore可以读取8种类型的数据
int、double、string、boolean、float、long、stringSet、byteArray.
初始化DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
上面一行代码意味着,通过委托模式、双重校验懒汉单例模式通过PreferenceDataStoreFactory生成了一个DataStore对象,
在/data/data/packageName/files/datastore/目录下生成一个名为setting,后缀为preferences_pb的文件,如下所示:
/data/data/edu.tyut.datastorelearn/files/datastore/settings.preferences_pb.
该文件内容如下:
content*让人
写入值
context.dataStore.edit { it: MutablePreferences ->
val contentKey: Preferences.Key<String> =
stringPreferencesKey(name = "content")
it[contentKey] = content
snackBarHostState.showSnackbar("保存成功!")
Log.i(TAG, "Greeting -> 读取: ${it[contentKey]}")
}
读取值
val flow: Flow<String> = context.dataStore.data.map { it: Preferences -> // Preferences -> value
val contentKey: Preferences.Key<String> =
stringPreferencesKey(name = "content")
it[contentKey] ?: "default"
}
coroutineScope.launch{
flow.collectLatest{ content: String ->
snackBarHostState.showSnackbar("content: $content")
}
}
Proto DataStore
他的使用比较复杂。
初始化DataStore
val Context.userPreferencesStore: DataStore<Person> by dataStore(
fileName = "person_prefs.pb",
serializer = PersonSerializer()
)
写入值
context.userPreferencesStore.updateData { person: Person ->
person.toBuilder().setUsername(userName)
.setAge(age.toIntOrNull() ?: 0)
.setMan(isMan)
.build().apply {
snackBarHostState.showSnackbar("保存成功Person: $this")
}
}
读取值
val flow: Flow<Person> =
context.userPreferencesStore.data.map { it: Person -> // Preferences -> value
it
}
coroutineScope.launch {
flow.collectLatest { person: Person ->
val username: String = String(bytes = person.username.toByteArray(charset = Charsets.UTF_8), charset = Charsets.UTF_8)
Log.i(TAG, "Greeting -> person: $person, username: $username")
snackBarHostState.showSnackbar("读取成功person: $person")
}
}
Protobuf会将中文字符按照UTF-8字符集被解码并且转换为8进制数字字符串,
toByteArray相当于"\350\277\231\346\230\257"(字符串) -> 0xE30xBF0x99 0xE60x980x97(数字)
然后以UTF-8编码为:这是.
完整代码示例:DataStoreLearn,文章水平有限,有什么不足还请大家指出,
But代码仓库中的代码质量还是有的😊。
TODO 封装 DataStore