最近項目中自己負責的模塊有涉及到緩存的內容,因爲項目中已經有了Sp的使用工具類,因爲一直想對它進行改進,可是一直沒有好的機會,這次是個契機,對DataStroe庫進行了簡單的使用,目前還在測試版本,還沒有穩定版,但是用着還不錯,本人先單一的在自己的負責的模塊使用,等到穩定版發佈再進行升級以及原有的Sp緩存數據的遷移,所以這裏也是先做個簡單的記錄,裏面也有自己的一些使用心得優化。
DataStore和Sp的對比,很多人還不是很明白,我也是簡單的介紹一下吧,Sp的弊端我們知道它會阻塞主線程,如果內容比較大那麼App的體驗會變得遲鈍,卡頓等現象,並且讀取出來的內容還會一值在內存中,App會變得更卡;DataStore庫是異步的,它不會阻塞主線程,我們看到它的方法使用都是帶有supend關鍵字,說明它支持協程一起使用,使用的時候會掛起,不會阻塞主線程,這也是它的優點,用起來也是非常方便,我先上一下我自己的代碼。
首先我們要創建緩存的對象:
var dataStore: DataStore<Preferences> = context.createDataStore( name = PREFERENCE_NAME )
name是我們的文件名字,然後我們就可以使用了。
我利用單例進行了工具類的封裝使用:
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)
我們知道在app裏面不同的用戶會進行不同緩存數據key進行標記,我們在根方法裏面直接傳入字符串進行preferencesKey的生成,所以我們看一下key的聲明的類:
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庫的另一種使用跟高級一些,過幾天我再進行總結!多謝觀看,歡迎留言!