2021-08-06 Jetpack 之 DataStore 介绍和工具类的封装,36 岁老码农现身说法
通过 getXXX()
获取值
的形式,可能会导致主线程阻塞
SharedPreferences
不能保证
类型安全
SharedPreferences 加载的数据会一直留在内存中,
浪费内存
apply()方法虽然是异步的,可能会发生
ANR
,在 8.0 之前和 8.0 之后实现各不相同
apply() 方法无法获取到操作成功或者
失败
的结果
(1)为什么 getXXX()方法会导致主线程阻塞
因为 getXXX()都是同步的,在主线程调用 get 方法时,同步方法内调用了 wait() 方法,会必须等待
getSharedPreferences()
方法开启的线程读取完数据完毕,才能继续往下执行,会导致主线程阻塞。如果数据量读取的小,并没有什么影响,如果读取的文件较大会导致主线程阻塞。
调用 getSharedPreferences() 方法,最终会调用
SharedPreferencesImpl#startLoadFromDisk() 方法开启一个线程异步读取数据。
(2)SharedPreferences 不能保证类型安全
调用 getXXX() 方法的时候,可能会出现 ClassCastException 异常,因为使用相同的 key
进行操作的时候,putXXX 方法可以使用不同类型的数据覆盖掉相同的 key。
(3)SharedPreferences 加载的数据会一直留在内存中
通过 getSharedPreferences() 方法加载的数据,最后会将数据存储在静态的成员变量中。 通过静态的 ArrayMap
缓存每一个 SharedPreferences 文件,而每个 SharedPreferences 文件内容通过 Map
缓存键值对数据,这样数据会一直留在内存中,浪费内存。
(4)apply()方法是异步的,可能会发生 ANR
apply() 方法是异步的,本身是不会有任何问题,但是当生命周期处于 handleStopService() 、
handlePauseActivity() 、 handleStopActivity() 的时候会一直等待 apply()
方法将数据保存成功,否则会一直等待,从而阻塞主线程造成 ANR。
DataStore 带来了哪些改变呢?
与其说 DataStore 相对 SharedPreference 的改变,不如说是 Preferences DataStore,因为 Preferences DataStore 主要是替换 SharedPreference 的,并且解决了 SharedPreference 所有问题
DataStore 是基于 Flow 实现的,所以保证了在主线程的安全性
以事务方式处理更新数据,事务有四大特性(原子性、一致性、 隔离性、持久性)
没有 apply() 和 commit() 等等数据持久的方法
自动完成 SharedPreferences 迁移到 DataStore,保证数据一致性,不会造成数据损坏
可以监听到操作成功或者失败结果
注意:
Preferences DataStore 只支持 Int , Long , Boolean , Float , String
键值对数据,适合存储简单、小型的数据,并且不支持局部更新,如果修改了其中一个值,整个文件内容将会被重新序列化,可以运行
AndroidX-Jetpack-Practice/DataStoreSimple 体验一下,如果需要局部更新,建议使用 Room。
附上一张 Google 分析的 SharedPreferences 和 DataStore 的区别:

附上一张 MMKV、DataStore、SharedPreferences 的区别

工具类的封装
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "data")
object DataStoreUtils {
/**
保存数据
*/
suspend fun <T : Any> put(context: Context, key: String, value: T) {
context.dataStore.edit { setting ->
when (value) {
is Int -> setting[intPreferencesKey(key)] = value
is Long -> setting[longPreferences
《Android 学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
Key(key)] = value
is Double -> setting[doublePreferencesKey(key)] = value
is Float -> setting[floatPreferencesKey(key)] = value
is Boolean -> setting[booleanPreferencesKey(key)] = value
is String -> setting[stringPreferencesKey(key)] = value
else -> throw IllegalArgumentException("This type can be saved into DataStore")
}
}
}
/**
获取数据
*/
suspend inline fun < reified T : Any> get(context: Context, key: String): T {
return when (T::class) {
Int::class -> {
context.dataStore.data.map { setting ->
setting[intPreferencesKey(key)] ?: 0
}.first() as T
}
Long::class -> {
context.dataStore.data.map { setting ->
setting[longPreferencesKey(key)] ?: 0L
}.first() as T
}
Double::class -> {
context.dataStore.data.map { setting ->
总结
写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于 Flutter 的学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的还有高级 UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter 全方面的 Android 进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。

评论