国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Android上的基于協(xié)程的存儲框架

這篇具有很好參考價值的文章主要介紹了Android上的基于協(xié)程的存儲框架。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

在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. 1.支持存儲和讀取
  2. 2.支持加密和解密
  3. 3.支持序列化和反序列化
  4. 4.支持多“倉庫"

由此我們定義了3組接口

  1. 1.Storage 存儲器
  2. 2.Serializer 序列化器
  3. 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):

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模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 【Android源碼面試寶典】MMKV從使用到原理分析(二)

    上一章節(jié),我們從使用入手,進行了MMKV的簡單講解,我們通過分析簡單的運行時日志,從中大概猜到了一些MMKV的代碼內部流程,同時,我們也提出了若干的疑問?還是那句話,帶著目標(問題)去閱讀一篇源碼,那么往往收獲的知識,更加深入扎實。 本節(jié),我們一起來從源

    2024年01月17日
    瀏覽(18)
  • Unity 的協(xié)程的原理

    Unity是一款非常強大的游戲引擎,它支持多種編程語言,其中最常用的語言是C#。在Unity中,協(xié)程是一種非常強大的功能,它可以讓我們在游戲中實現(xiàn)各種各樣的效果。本文將詳細介紹Unity協(xié)程的原理,并給出示例代碼詳解。 對啦!這里有個游戲開發(fā)交流小組里面聚集了一幫熱

    2024年02月02日
    瀏覽(36)
  • golang 協(xié)程的實現(xiàn)原理

    golang 協(xié)程的實現(xiàn)原理

    要理解協(xié)程的實現(xiàn), 首先需要了解go中的三個非常重要的概念, 它們分別是 G ,? M 和 P , 沒有看過golang源代碼的可能會對它們感到陌生, 這三項是協(xié)程最主要的組成部分, 它們在golang的源代碼中無處不在. G (goroutine) G是goroutine的頭文字, goroutine可以解釋為受管理的輕量線程, gorout

    2024年02月10日
    瀏覽(21)
  • 【Kotlin】協(xié)程的字節(jié)碼原理

    【Kotlin】協(xié)程的字節(jié)碼原理

    前言 協(xié)程是Koltin語言最重要的特性之一,也是最難理解的特性。網上關于kotlin協(xié)程的描述也是五花八門,有人說它是輕量級線程,有人說它是無阻塞式掛起,有人說它是一個異步框架等等,眾說紛蕓。甚至還有人出了書籍專門介紹kotlin協(xié)程。 筆者剛開始接觸這個概念也是一

    2024年01月18日
    瀏覽(18)
  • [Kotlin Tutorials 21] 協(xié)程的取消

    [Kotlin Tutorials 21] 協(xié)程的取消

    本文討論協(xié)程的取消, 以及實現(xiàn)時可能會碰到的幾個問題. 本文屬于合輯: https://github.com/mengdd/KotlinTutorials 取消的意義: 避免資源浪費, 以及多余操作帶來的問題. 基本特性: cancel scope的時候會cancel其中的所有child coroutines. 一旦取消一個scope, 你將不能再在其中l(wèi)aunch新的coroutine. 一

    2024年02月08日
    瀏覽(14)
  • Kotlin協(xié)程的JVM實現(xiàn)源碼分析(上)

    本文從協(xié)程的啟動 launch 源碼入手分析,協(xié)程JVM實現(xiàn)分為兩篇: 協(xié)程啟動和執(zhí)行源碼分析 無棧協(xié)程 和 Continuation 基本環(huán)境: IntelliJ IDEA 2023.3.2 Kotlin 1.8.20 kotlinx-coroutines-core 1.7.3 gradle 8.2 以 GlobalScope.launch 啟動協(xié)程分析: 調用關系: CoroutineScope.launch - StandaloneCoroutine.start - Corou

    2024年01月19日
    瀏覽(24)
  • Kotlin協(xié)程的JVM實現(xiàn)源碼分析(下)

    Kotlin協(xié)程的JVM實現(xiàn)源碼分析(下)

    協(xié)程 根據 是否保存切換 調用棧 ,分為: 有棧協(xié)程(stackful coroutine) 無棧協(xié)程(stackless coroutine) 在代碼上的區(qū)別是:是否可在普通函數里調用,并暫停其執(zhí)行。 Kotlin協(xié)程,必須在掛起函數中調用和恢復,屬于 無棧協(xié)程 。 常見的語言,協(xié)程實現(xiàn): 有棧協(xié)程 :Go、Lua 無棧

    2024年01月23日
    瀏覽(17)
  • Kotlin: 協(xié)程的四種啟動模式(CoroutineStart)

    Kotlin: 協(xié)程的四種啟動模式(CoroutineStart)

    點擊查看CoroutineStart英文文檔 創(chuàng)建協(xié)程的三種方式 runBlocking 運行一個協(xié)程并且會阻塞當前線程,直到它完成。 launch 啟動一個新的協(xié)程,不會阻塞當前線程,并且返回一個Job,可以取消。 async async和await是兩個函數,這兩個函數在我們使用過程中一般都是成對出現(xiàn)的。 async用

    2024年04月23日
    瀏覽(22)
  • Unity中停止協(xié)程的多種方式解析

    在Unity3D游戲開發(fā)中,協(xié)程(Coroutine)是一種非常有用的功能,可以在游戲中實現(xiàn)延遲執(zhí)行、定期執(zhí)行和異步操作等任務。然而,有時候我們需要在運行時停止協(xié)程的執(zhí)行。本文將介紹Unity中停止協(xié)程的幾種常用方式,并提供相應的源代碼示例。 使用StopCoroutine函數停止協(xié)程

    2024年02月03日
    瀏覽(22)
  • Unity 之 錯誤的停止協(xié)程的方式

    Unity 之 錯誤的停止協(xié)程的方式

    相信很多人都會這樣開啟一個協(xié)程 這樣確實沒啥毛病,那么怎么關掉這個協(xié)程呢,是不是在想也是一樣的傳cor_1()這個參數,然后start對應stop,試著輸入stopCor....誒,代碼提示有這個方法喔,然后寫下了這樣的代碼 結果你會發(fā)現(xiàn)這個協(xié)程并沒有被停下來。。。那該咋辦呢?我在

    2024年02月16日
    瀏覽(17)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包