最近項目中本身負責的模塊有涉及到緩存的內容,由於項目中已經有了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庫的另外一種使用跟高級一些,過幾天我再進行總結!多謝觀看,歡迎留言!