写点什么

2021-08-06 Jetpack 之 DataStore 介绍和工具类的封装,36 岁老码农现身说法

作者:嘟嘟侠客
  • 2021 年 11 月 27 日
  • 本文字数:1996 字

    阅读完需:约 7 分钟

  • 通过 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 进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。



本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

用户头像

嘟嘟侠客

关注

还未添加个人签名 2021.03.19 加入

还未添加个人简介

评论

发布
暂无评论
2021-08-06 Jetpack之DataStore介绍和工具类的封装,36岁老码农现身说法