赞
踩
Jetpack DataStore是一种用来
替换SharedPreferences
新型数据存储解决方案。
以异步
、一致的事务
方式存储数据,DataStore
保证原子性,一致性,隔离性,持久性。它是线程安全,且非阻塞的,DataStore在使用上强制开发者将其放在协程
中进行调用,保证了主线程的安全,避免SharedPreferences在主线程可能会引发ANR问题。
总之,它克服了 SharedPreferences 的一些缺点,解决了 SharedPreferences API 的设计缺陷。
Jetpack DataStore是以
Kotlin协程
和Flow
功能为基础提供了两种方式:
Proto DataStore
存储类的对象,通过 protocol buffers
将对象序列化
存储在本地。Preferences DataStore
以键值对
的形式存储在本地。(和SharedPreferences类似,基于 Flow实现的,不会阻塞主线程,保证类型安全)- 支持从SharedPreferences到DataStore的数据迁移。
SharedPreferences的缺点
其实SharedPreferences作为一种轻量级的数据存储方式,使用起来也非常方便,以键值对的形式存储在本地,初始化 SharedPreference 的时候,会将整个文件内容加载内存中,因此会带来以下问题:
- 通过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 是基于 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[longPreferencesKey(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 -> setting[doublePreferencesKey(key)] ?:0.0 }.first() as T } Float::class -> { context.dataStore.data.map { setting -> setting[floatPreferencesKey(key)] ?:0f }.first() as T } Boolean::class -> { context.dataStore.data.map { setting -> setting[booleanPreferencesKey(key)]?:false }.first() as T } String::class -> { context.dataStore.data.map { setting -> setting[stringPreferencesKey(key)] ?: "" }.first() as T } else -> { throw IllegalArgumentException("This type can be get into DataStore") } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。