当前位置:   article > 正文

DataStore入门及在项目中的使用_datastore使用

datastore使用

首先给个官网的的地址:应用架构:数据层 - DataStore - Android 开发者  |  Android Developers

小伙伴们可以直接看官网的资料,本篇文章是对官网的部分细节进行补充

一、为什么要使用DataStore 代替SharedPreferences

SharedPreferences:

  • DataStore出现之前,我们用的最多的存储方式毫无疑问是SP,其使用方式简单、易用,广受好评。然而google对SP的定义为轻量级存储,如果存储的数据少,使用起来没有任何问题,当需要存储数据比较多时,SP可能会导致以下问题:
  • 1. SP第一次加载数据时需要全量加载,当数据量大时可能会阻塞UI线程造成卡顿;
  • 2. SP读写文件不是类型安全的,且没有发出错误信号的机制,缺少事务性API;
  • 3. commit() / apply()操作可能会造成ANR问题;
  • commit()是同步提交,会在UI主线程中直接执行IO操作,当写入操作耗时比较长时就会导致UI线程被阻塞,进而产生ANR;apply()虽然是异步提交,但异步写入磁盘时,如果执行了Activity / Service中的onStop()方法,那么一样会同步等待SP写入完毕,等待时间过长时也会引起ANR问题。

DataStore:

  • DataStore基于事务方式处理数据更新。

  • DataStore基于Kotlin Flow存取数据,默认在Dispatchers.IO里异步操作,避免阻塞UI线程,且在读取数据时能对发生的Exception进行处理。

  • 不提供apply()、commit()存留数据的方法。

  • 支持SP一次性自动迁移至DataStore中

总结:SharedPreferences卡进程,DataStore不卡进程

二、如何使用

  1. 添加依赖,在build.grade(:app)中添加:
    implementation("androidx.datastore:datastore-preferences:1.0.0")
    
  2. 创建 Preferences DataStore
    1. private val Context.mDataStore: DataStore<Preferences> by preferencesDataStore(name = "appData")
    2. private val mContext =MyApplication.mContext

    使用属性委托来创建,方便访问,并且可以保留为单例,只需要传入一个数据库名称即可,点开preferencesDataStore查看源码可以看到,只需要一个name参数,其他参数都是非必填,当初看到这个Context.mDataStore写法对新手有点蒙,各位小伙伴可以了解一下属性委托再看这个代码,preferencesDataStore返回的是PreferenceDataStoreSingletonDelegate,它的getValue方法如下,“委托必须提供一个操作符函数 getValue(),该函数具有以下参数:  thisRef —— 必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是其超类型。 property —— 必须是类型 KProperty<*> 或其超类型“。这样我们就知道为啥使用Context加扩展写法了,如果直接在Application这样有上下文Context的地方可以直接写mDataStore而不是Context.mDataStore

    override fun getValue(thisRef: Context, property: KProperty<*>): DataStore<Preferences>
  3. 使用相应的键类型函数为需要存储在 DataStore<Preferences> 实例中的每个值定义一个键。例如,如需为 int 值定义一个键,请使用 intPreferencesKey()。通常我们项目中需要存储类似userId,userName类似字段,我以它们两个举例说明:

    1. private val USER_ID = intPreferencesKey("userId")
    2. private val USER_NAME = stringPreferencesKey("userName")
  4. 存入数据:Preferences DataStore 提供了一个 edit() 函数,用于以事务方式更新 DataStore 中的数据。该函数的 transform 参数接受代码块,您可以在其中根据需要更新值。转换块中的所有代码均被视为单个事务。
    1. private suspend fun setUserName(userName: String) {
    2. mContext.mDataStore.edit { settings ->
    3. settings[USER_NAME] = userName
    4. }
    5. }
    6. private suspend fun setUserId(userId: Int) {
    7. mContext.mDataStore.edit { settings ->
    8. settings[USER_ID] = userId
    9. }
    10. }
  5. 读取数据:(我直接取Flow的第一个值)
    1. private suspend fun getUserId() {
    2. mUserId = mDataStore.data.map { preferences ->
    3. preferences[USER_ID] ?: 0
    4. }.first()
    5. }
    6. private suspend fun getUserName() {
    7. mUserName = mDataStore.data.map { preferences ->
    8. preferences[USER_NAME] ?: "empty"
    9. }.first()
    10. }
  6. 如果是在Activity中可以直接使用lifecycleScope

    1. lifecycleScope.launch {
    2. getUserId()
    3. }
  7. 如果想在整个应用中随时调用,不跟随某个组件的生命周期,可以在Application中声明一个全局的协程作用域,注意释放

    1. override fun onCreate() {
    2. mApplicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
    3. mContext = applicationContext
    4. }
    5. companion object {
    6. var mContext: Context by Delegates.notNull()
    7. var mApplicationScope: CoroutineScope by Delegates.notNull()
    8. }
    9. override fun onLowMemory() {
    10. super.onLowMemory()
    11. mApplicationScope.cancel()
    12. }

    这样我们可以使用mApplicationScope随时存储和读取数据啦

  8. 我们不想读取和存储数据是异步操作,比如我们的网络请求一header里开始就需要userID和username判断用户是否登录,我们可以使用runBlocking阻塞进程

    1. runBlocking {
    2. //此处取Flow都是取第一个值,可以写两个取值操作,
    3. DataStoreUtil.getUserId()
    4. DataStoreUtil. getUserName()
    5. //如果是没有调用flow.first()方法,无法取到username,可以把取值放在不同协程中,Flow就不会相互干扰了
    6. // launch {
    7. // getUserId()
    8. // }
    9. // launch {
    10. // getUserName()
    11. // }
    12. }
  9. 附上代码地址:GitHub - scYao/DataStoreDmeo: DataStore Demo

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/419515
推荐阅读
  

闽ICP备14008679号