在Android上,經常會需要持久化本地數據,比如我們需要緩存用戶的配置信息、用戶的數據、緩存數據、離線緩存數據等等。我們通常使用的工具為SharePreference、MMKV、DataStore、Room、文件等等。通過使用現(xiàn)有的存儲框架,結合協(xié)程,我們可以方便地實現(xiàn)一個輕量級的響應式存儲框架。
在使用的場景上,我們使用Key-Value的場景很多,而且我們往往不僅僅是存儲數據、獲取數據,經常還有需要序列化存儲、加密存儲、訂閱數據的變化的功能。
訂閱數據的變化,常見的就是使用發(fā)布/訂閱模式來實現(xiàn)。
但是使用類如EventBus和RxBus并不是一個好的實踐,EventBus沒有做適當的封裝被濫用的話,會導致邏輯混亂,難以跟蹤,并且調試起來也相當困難。
谷歌的DataStore就是一個很好的實現(xiàn)。除了DataStore,我們其實也可以使用基于現(xiàn)有的SharePreference、MMKV通過協(xié)程等來實現(xiàn)我們的響應式存儲框架。
下面我們就來設計這個存儲框架。
首先我們基于我們的功能來定義我們的接口
我們的功能如下
- 1.支持存儲和讀取
- 2.支持加密和解密
- 3.支持序列化和反序列化
- 4.支持多“倉庫"
由此我們定義了3組接口
- 1.Storage 存儲器
- 2.Serializer 序列化器
- 3.CryptoHandler 加密和解密處理器
在清潔架構的分層中,存儲(Storage)是屬于一種"接口適配器",因為它為應用的內部業(yè)務邏輯(即領域層)提供了與外部世界(即數據庫、網絡、文件系統(tǒng)等)的接口。一般在Respository中和這些接口適配器進行通訊來獲取和存儲數據,所以在設計Storage的時候,我們應該遵循下面的概念。
Storage接口定義了一個抽象的存儲協(xié)議,不關注具體的實現(xiàn)方式,例如使用SharedPreferences,MMKV,或者DataStore,這正是適配器層的職責。通過適配器層,我們可以使得業(yè)務邏輯從具體的技術細節(jié)中解耦,使其更關注于應用的業(yè)務規(guī)則,而不是底層的存儲細節(jié)。
同時,我們的設計要允許我們根據需要,靈活地更換或者修改存儲的具體實現(xiàn),而無需改動業(yè)務邏輯或者其他部分的代碼。
而這正是清潔架構的一個重要原則:獨立性和隔離變化,即依賴抽象而不是具體實現(xiàn)。
基于此設計如下的存儲器接口
interface Storage {
fun put( key:String, obj:Any?)
operator fun <T> get( key: String, classOfT:Class<T>):T?
operator fun <T> get( key: String, typeOfT: Type):T?
fun contain( key: String):Boolean
fun onKeyChanged( key:String): Flow<String>
fun remove( key: String)
fun removeAllPrefix( prefixKey:String )
fun removeExcludePrefix( vararg prefixKey: String )
fun clear()
}
inline operator fun <reified T> Storage.get(key: String): T? {
return get(key, T::class.java)
}
Storage接口設計將基本的存儲操作抽象化,并通過onKeyChanged提供了數據變化的通知,這是一個非常有用的功能,使得可以對存儲數據的改變進行反應。
此外,removeAllPrefix和removeExcludePrefix方法也為更精細的數據控制提供了可能性,這在處理具有特定前綴鍵值對的場景中非常有用。
Storage接口設計的目的是為了隱藏實現(xiàn)細節(jié)和提高代碼的可讀性、可維護性和可擴展性。
下面我們基于此繼續(xù)擴展我們的Storage功能
首先,我們的數據我們希望是序列化存儲的,并且可以支持加密。
因此我們繼續(xù)定義接口:
interface Serializer {
fun serialize(obj: Any): String
fun <T> deserialize(obj: String, classOfT: Class<T>): T
fun <T> deserialize(obj: String, typeOfT: Type): T
}
inline fun <reified T> Serializer.deserialize(obj: String): T = deserialize(obj, T::class.java)
然后是加密和解密接口:
interface CryptoHandler {
fun encrypt(obj: String): String
fun decrypt(obj: String): String
}
接下來我們就可以使用這兩個接口來執(zhí)行序列化、反序列化,加密和解密的操作。
首先MMKV是支持加密的,但是MMKV使用的是AES CFB-128加密算法來做的。但是它并不是那么足夠安全,它沒有提供硬件級別的安全加密方法。所以可以考慮自己使用Android KeyStore 來實現(xiàn)硬件級別的加密。
使用Android Keystore來實現(xiàn),一般大致思路就是拿使用Android的keystore 創(chuàng)建一組加密對密鑰,然后使用AES算法來加密和解密。
序列化我們可以使用ProtoBuf或者是json來實現(xiàn)
下面簡單使用gson來實現(xiàn)我們的序列化存儲如下:
@Singleton
open class JsonSerializer(private val gson: Gson) : Serializer {
override fun serialize(obj: Any): String {
return gson.toJson(obj)
}
override fun <T> deserialize(obj: String, classOfT: Class<T>): T {
return gson.fromJson(obj, classOfT)
}
override fun <T> deserialize(obj: String, typeOfT: Type): T {
return gson.fromJson(obj, typeOfT)
}
}
定義好了接口,實現(xiàn)起來就很簡單了,只需要在修改key-value的時候,發(fā)送一個key被修改的消息到一個flow,對flow的訂閱者就可以訂閱數據的改變了。
接下來我們基于MMKV和SharePreference來實現(xiàn)這個存儲接口
首先我們來使用SharePreference和MMKV來實現(xiàn)這個存儲功能
?
class SharePreferenceStorage (
private val context: Context,
private val storageType: StorageType,
private val serializer: Serializer,
private val eventLogger: StorageLogger?,
private val cryptoHandler: CryptoHandler?):Storage{
private val sharedPreferences: SharedPreferences =
context.getSharedPreferences(storageType.alias, Context.MODE_PRIVATE)
private val keyChangedFlow = MutableSharedFlow<String>(replay = 100)
override fun put(key: String, obj: Any?) {
obj?.let {data->
sharedPreferences.edit().let {editor->
editor.putString( key , serializer.serialize( data ).let {
cryptoHandler?.encrypt( it )?:it
} )
editor.apply()
keyChangedFlow.tryEmit( key )
eventLogger?.trackEvent(StorageSaveEvent( getStorageName(),key, cryptoHandler != null))
}
}?: run {
remove(key)
}
}
override fun <T> get(key: String, classOfT: Class<T>): T? {
sharedPreferences.getString( key ,null )?.let {
cryptoHandler?.decrypt( it )?:it
}?.let {
eventLogger?.trackEvent(StorageLoadEvent( getStorageName(),key, true))
serializer.deserialize( it ,classOfT)
}?.let {
return it
}?:run{
return null
}
}
override fun <T> get(key: String, typeOfT: Type): T? {
val serializeString = sharedPreferences.getString( key ,null )?.let {
cryptoHandler?.decrypt( it )?:it
}
return serializeString?.let {
serializer.deserialize( it ,typeOfT)
}
}
override fun onKeyChanged(key: String): Flow<String> {
return keyChangedFlow.asSharedFlow()
}
override fun contains(key: String): Boolean {
return sharedPreferences.contains( key )
}
override fun remove(key: String) {
if( contains( key ) ){
sharedPreferences.edit().let {editor->
editor.remove( key )
editor.apply()
keyChangedFlow.tryEmit( key )
eventLogger?.trackEvent(StorageRemoveEvent( getStorageName(),key))
}
}
}
override fun removeAllPrefix(prefixKey: String) {
sharedPreferences.all?.let {allData->
allData.keys.filter { it.startsWith( prefixKey ) }.forEach {
remove( it )
}
}
}
override fun removeExcludePrefix(vararg prefixKey: String) {
sharedPreferences.all?.let {allData->
val prefixSet = prefixKey.toSet()
val allKeys = allData.keys
allKeys.forEach { key ->
if (prefixSet.none { key.startsWith(it) }) {
remove(key)
}
}
}
}
override fun clear() {
sharedPreferences.edit().let {editor->
sharedPreferences.all.keys.forEach {
remove( it )
}
keyChangedFlow.tryEmit( CLEAR_CACHE )
eventLogger?.trackEvent(StorageClearEvent( getStorageName()))
}
}
private fun getStorageName():String{
return "SharePreference-${storageType.alias}"
}
}
下面是基于MMKV的實現(xiàn):文章來源:http://www.zghlxwxcb.cn/news/detail-639006.html
class MMKVStorage constructor(
private val storageType: StorageType,
private val serializer: Serializer,
private val eventLogger: StorageLogger?,
private val cryptoHandler: CryptoHandler?): Storage {
private val mmkv: MMKV = MMKV.mmkvWithID( storageType.alias, MMKV.MULTI_PROCESS_MODE)
private val keyChangedFlow = MutableSharedFlow<String>(replay = 100)
private val subscribeKeyList:MutableList<String> = mutableListOf()
override fun put(key: String, obj: Any?) {
obj?.let {
val serializerObj = serializer.serialize( obj ).let {
cryptoHandler?.encrypt( it )?:it
}
mmkv.encode( key,serializerObj)
keyChangedFlow.tryEmit(key)
eventLogger?.trackEvent(StorageSaveEvent( getStorageName(),key, cryptoHandler != null))
} ?: run{
remove(key)
}
}
override fun <T> get(key: String, classOfT: Class<T>): T? {
return mmkv.decodeString( key )?.let{ jsonString->
eventLogger?.trackEvent(StorageLoadEvent( getStorageName(),key, true))
serializer.deserialize(jsonString.let {
cryptoHandler?.decrypt(it)?:it
},classOfT)
}
}
override fun <T> get(key: String, typeOfT: Type): T? {
return mmkv.decodeString( key)?.let { jsonString->
eventLogger?.trackEvent(StorageLoadEvent( getStorageName(),key, true))
serializer.deserialize( jsonString.let {
cryptoHandler?.decrypt(it)?:it
}, typeOfT)
}
}
override fun onKeyChanged(key: String): Flow<String> {
subscribeKeyList.add(key)
return keyChangedFlow.asSharedFlow().filter { it == key }
}
override fun contains(key: String): Boolean {
return mmkv.containsKey( key )
}
override fun remove(key: String) {
mmkv.remove(key).apply()
eventLogger?.trackEvent(StorageRemoveEvent( getStorageName(),key))
keyChangedFlow.tryEmit( key )
}
override fun removeAllPrefix( prefixKey:String ){
val allKeys = mmkv.allKeys()?.clone()?: emptyArray()
allKeys.forEach { if( it.contains(prefixKey)) remove(it) }
}
override fun removeExcludePrefix(vararg prefixKey: String) {
val allKeys = mmkv.allKeys()?.clone() ?: emptyArray()
val prefixSet = prefixKey.toSet()
allKeys.forEach { key ->
if (prefixSet.none { key.startsWith(it) }) {
remove(key)
}
}
}
override fun clear() {
mmkv.allKeys()?.forEach {
remove(it)
}
keyChangedFlow.tryEmit( Storage.CLEAR_CACHE )
mmkv.clearAll()
eventLogger?.trackEvent(StorageClearEvent( getStorageName()))
}
private fun getStorageName():String {
return "mmkv-${storageType.alias}"
}
}
通過上面的代碼,我們就可以實現(xiàn)訂閱數據的改變。文章來源地址http://www.zghlxwxcb.cn/news/detail-639006.html
到了這里,關于Android上的基于協(xié)程的存儲框架的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!