前言
- 本篇主要介绍DataStore,包含介绍,使用等。
- 本篇会介绍DataStore两种实现Preferences DataStore(主要介绍)和Proto DataStore(后续介绍)。
- 本篇也会介绍到DataStore和SharedPreferences的不同之处,以及如何进行迁移。
简介
- 首先,DataStore是Jetpack一部分,是一种数据存储解决方案。
- 其次,DataStore使用协程及flow以异步、一致的方式实现数据的存储。
- 最后是DataStore的实现,分为Preferences DataStore和Proto DataStore:
- Preferences DataStore 类似于SharedPreferences,键值对存储,本篇的主要介绍。
- Proto DataStore 将数据作为自定义数据类型的实例进行存储,基于Google protobuf实现。
DataStore和SharedPreferences(SP)
- SP存在的问题:
- 内存浪费问题,加载的数据会一直存在在内存中。
- get方法可能会阻塞主线程。
- apply方法虽然为异步,也可能会发生ANR。
- SP也无法保证类型安全。
- SP不支持跨进程。
- DataStore的特点:
- 基于协程和Flow实现,保证了主线程的安全性。
- 以事务的方式进行处理,保证了操作的原子性、一致性、隔离性及持久性。
- Preferences DataStore可以支持SP的迁移,保证数据的完整性。
- 说明:
- 此处只是列出SP存在的问题,并没有SP一无是处的的说法,DataStore在SP的基础上解决了不少的问题,但是也没有说SP存在的问题已经全部解决了。
- 至少,DataStore在SP的基础上解决了如类型安全、阻塞导致anr等问题,确实这方面优于SP。
Preferences DataStore
1. 依赖引入及扩展
- 依赖引入
implementation 'androidx.datastore:datastore-preferences:1.0.0'
- 扩展属性,对Contex扩展属性,方便使用。
val Context.dataStore by preferencesDataStore(name = "data_store")
2. 基本使用:
编码
//存储
dataStore.edit { edit ->
edit[stringPreferencesKey("data")] = "123456"
}
//读取
val data = dataStore.data.first()[stringPreferencesKey("data")]
Log.d(TAG.TAG,"data is $data")
日志
2022-08-04 14:38:15.606 13419-13445/edu.test.demo D/Test-TAG: data is 123456
分析:
- 一眼看起来貌似和SP区别也不大,事实上使用起来区别也确实不大。
- 区别在于key值不再是String,而是Preferences.Key<String>,当然这是存储string的key,存储别的类型key也不一样。
- 有一点就是必须在协程中使用,因为edit方法是suspend方法,读取的时候data返回值为flow,操作起来也是在协程中。
3. 更新:
代码1(也就是再编辑一遍就覆盖了)如下:
//存储
dataStore.edit { edit ->
edit[stringPreferencesKey("data")] = "123456"
}
//读取
val data = dataStore.data.first()[stringPreferencesKey("data")]
Log.d(TAG.TAG,"data is $data")
dataStore.edit { edit ->
edit[stringPreferencesKey("data")] = "123456789"
}
Log.d(TAG.TAG,"data is ${ dataStore.data.first()[stringPreferencesKey("data")]}")
日志如下:
2022-08-04 14:44:25.651 13552-13579/edu.test.demo D/Test-TAG: data is 123456
2022-08-04 14:44:25.657 13552-13579/edu.test.demo D/Test-TAG: data is 123456789
代码2 调用update
//存储
dataStore.edit { edit ->
edit[stringPreferencesKey("data")] = "123456"
}
//读取
val data = dataStore.data.first()[stringPreferencesKey("data")]
Log.d(TAG.TAG,"data is $data")
dataStore.updateData {
val mutablePreference = it.toMutablePreferences()
mutablePreference[stringPreferencesKey("data")] = "123456789"
mutablePreference.toPreferences()
}
Log.d(TAG.TAG,"data is ${ dataStore.data.first()[stringPreferencesKey("data")]}")
分析:
- 修改和SP也没有太大的却别,我们可以直接edit覆盖,也可以update。
3. DataStore支持的存储类型:
DataStore支持的存储类型从key上就可以看出,主要有七种,key类型以及调用方法如下,一眼就能看出来,就不一一
解释了:
- Preferences.Key<Int> intPreferencesKey
- Preferences.Key<Double> doublePreferencesKey
- Preferences.Key<String> stringPreferencesKey
- Preferences.Key<Boolean> booleanPreferencesKey
- Preferences.Key<Float> floatPreferencesKey
- Preferences.Key<Long> longPreferencesKey
- Preferences.Key<Set<String>> stringSetPreferencesKey
4. SP到DataStore的迁移
迁移编码:
- 主要是在扩展进行迁移,传入sp的文件名。
val Context.dataStore : DataStore<Preferences> by preferencesDataStore(name = "dataStore_setting",
produceMigrations = { context ->
listOf( SharedPreferencesMigration(context, "sp_data"))
})
迁移之前我们可以看下sp文件,位置为data/data/包名/shared_prefs/sp文件名:
迁移之后,sp文件被删除,datastore文件出现,如下:
验证编码:
val data = dataStore.data.first()[stringPreferencesKey("mydata")]
Log.d(TAG.TAG,"sp->datastore,直接读取:$data")
日志如下:
2022-08-04 15:22:24.062 14131-14157/edu.test.demo D/Test-TAG: sp->datastore,直接读取:mydata123456
分析:
- 对比可以看出,迁移之后sp文件被删除,出现datastore文件。
- 编码验证说明已经迁移成功,sp之前存储的值,我们在datastore直接读取了出来。
- 只有在我们使用dataStore.data.first()的时候,才会进行文件的迁移。
总结
- 本篇主要介绍了DataStore的实现分类。
- 本篇也介绍了DataStore在sp的基础上做了那些优化。
- 介绍了DataStore的使用,支持数据类型,以及SP到DataStore的迁移。