最近项目中本身负责的模块有涉及到缓存的内容,由于项目中已经有了Sp的使用工具类,由于一直想对它进行改进,但是一直没有好的机会,此次是个契机,对DataStroe库进行了简单的使用,目前还在测试版本,尚未稳定版,可是用着还不错,本人先单一的在本身的负责的模块使用,等到稳定版发布再进行升级以及原有的Sp缓存数据的迁移,因此这里也是先作个简单的记录,里面也有本身的一些使用心得优化。缓存
DataStore和Sp的对比,不少人还不是很明白,我也是简单的介绍一下吧,Sp的弊端咱们知道它会阻塞主线程,若是内容比较大那么App的体验会变得迟钝,卡顿等现象,而且读取出来的内容还会一值在内存中,App会变得更卡;DataStore库是异步的,它不会阻塞主线程,咱们看到它的方法使用都是带有supend关键字,说明它支持协程一块儿使用,使用的时候会挂起,不会阻塞主线程,这也是它的优势,用起来也是很是方便,我先上一下我本身的代码。app
首先咱们要建立缓存的对象:异步
var dataStore: DataStore<Preferences> = context.createDataStore( name = PREFERENCE_NAME )
name是咱们的文件名字,而后咱们就能够使用了。ide
我利用单例进行了工具类的封装使用:函数
class DataStoreRepository(val context: Context) : IDataStoreRepository { private val PREFERENCE_NAME = "DataStore" companion object { @Volatile private var instance: IDataStoreRepository? = null fun getInstance() = instance ?: synchronized(this) { instance ?: DataStoreRepository(OverAllConstant.context).also { instance = it } } }
IDataStoreRepository接口声明了咱们能用到的读取操做方法:工具
interface IDataStoreRepository { suspend fun saveIntData(key: String, value: Int) suspend fun readIntData(key: String): Int fun migrationSP2DataStore() }
首先介绍一下读取方法:测试
override suspend fun readIntData(key: String): Int { val KEY_PREFERENCES = preferencesKey<Int>("${key}${StateContext.getMemeberId()}") val value = dataStore.data.catch { if (it is IOException) { it.printStackTrace() emit(emptyPreferences()) } else { throw it } }.map { preferences -> preferences[KEY_PREFERENCES] ?: 0 } return value.first() }
我这里只是拿了Int的读取方法来作案例。优化
val KEY_PREFERENCES = preferencesKey<Int>("${key}${StateContext.getMemeberId()}")
首先来讲一下这行代码,咱们发现DataStore库的key不是Sp那样的简单的字符串来进行标记,咱们用对象进行包装的,preferencesKey<类型>(key:String)this
咱们知道在app里面不一样的用户会进行不一样缓存数据key进行标记,咱们在根方法里面直接传入字符串进行preferencesKey的生成,因此咱们看一下key的声明的类:spa
object PreferencesKeys { //原有的sp文件名字,用来和DataStore进行合并 val PREFERENCE_NAME = "preferences" //消息未读数 val KEY_MSG_UNREAD_NUM = "msg_unread_num" }
很简单,跟之前的使用同样,直接声明常亮字符串便可,因此咱们的变化就只能在跟方法里面本身去拿到用户的惟一标识来进行key的生成,这样就能够作到惟一了。
咱们再接下来看,catch方法,为了防止问题产生,咱们进行catch处理,当读取数据遇到错误的时候,若是是IO异常,那就发送一个空的preferences,来从新使用,若是是其余的异常就抛出去,不隐藏问题。
而后经过map方法来获取内容Folw<R>对象,最后经过first()方法返回Folw里面的内容,也就是咱们最终读取到数值。
咱们的方法都是用supend关键字来修饰,因此使用的地方都得在协程里面进行调用,首先咱们写一个扩展函数:
fun BaseActivity.initiateCoroutineScope( block: suspend () -> Unit ) { lifecycleScope.launch { runCatching { block() }.onSuccess { }.onFailure { it.printStackTrace() } } }
可见到方法里面咱们还进行了catch处理,错误也会打印出来,那么咱们在Activity里面使用的时候就很简单了,不用再从新写启动协程的代码,直接以下便可:
initiateCoroutineScope{
val value = DataStoreRepository.getInstance().readIntData(PreferencesKeys.KEY_MSG_UNREAD_NUM)
}
是否是很简单呢,这也是扩展函数的好处,代码清晰易懂,还有效的缩短了代码行数,不忽悠冗余的代码。
存储也是很简单:
initiateDataStore { DataStoreRepository.getInstance().saveIntData(PreferencesKeys.KEY_MSG_UNREAD_NUM, 100) }
咱们只须要用咱们写得扩展函数包装便可,直接调用save方法便可。
基本上的会用也就这些了,DataStore库的另外一种使用跟高级一些,过几天我再进行总结!多谢观看,欢迎留言!