当前位置: 首页 > news >正文

网站建设制作优帮云湖南搜索引擎推广平台

网站建设制作优帮云,湖南搜索引擎推广平台,做任务赚佣金的平台,如何架设内部网站在Android上,经常会需要持久化本地数据,比如我们需要缓存用户的配置信息、用户的数据、缓存数据、离线缓存数据等等。我们通常使用的工具为SharePreference、MMKV、DataStore、Room、文件等等。通过使用现有的存储框架,结合协程,我…

在Android上,经常会需要持久化本地数据,比如我们需要缓存用户的配置信息、用户的数据、缓存数据、离线缓存数据等等。我们通常使用的工具为SharePreference、MMKV、DataStore、Room、文件等等。通过使用现有的存储框架,结合协程,我们可以方便地实现一个轻量级的响应式存储框架。

在使用的场景上,我们使用Key-Value的场景很多,而且我们往往不仅仅是存储数据、获取数据,经常还有需要序列化存储、加密存储、订阅数据的变化的功能。

订阅数据的变化,常见的就是使用发布/订阅模式来实现。

但是使用类如EventBus和RxBus并不是一个好的实践,EventBus没有做适当的封装被滥用的话,会导致逻辑混乱,难以跟踪,并且调试起来也相当困难。

谷歌的DataStore就是一个很好的实现。除了DataStore,我们其实也可以使用基于现有的SharePreference、MMKV通过协程等来实现我们的响应式存储框架。

下面我们就来设计这个存储框架。

首先我们基于我们的功能来定义我们的接口

我们的功能如下

  1. 1.支持存储和读取
  2. 2.支持加密和解密
  3. 3.支持序列化和反序列化
  4. 4.支持多“仓库"

由此我们定义了3组接口

  1. 1.Storage 存储器
  2. 2.Serializer 序列化器
  3. 3.CryptoHandler 加密和解密处理器

在清洁架构的分层中,存储(Storage)是属于一种"接口适配器",因为它为应用的内部业务逻辑(即领域层)提供了与外部世界(即数据库、网络、文件系统等)的接口。一般在Respository中和这些接口适配器进行通讯来获取和存储数据,所以在设计Storage的时候,我们应该遵循下面的概念。

Storage接口定义了一个抽象的存储协议,不关注具体的实现方式,例如使用SharedPreferences,MMKV,或者DataStore,这正是适配器层的职责。通过适配器层,我们可以使得业务逻辑从具体的技术细节中解耦,使其更关注于应用的业务规则,而不是底层的存储细节。

同时,我们的设计要允许我们根据需要,灵活地更换或者修改存储的具体实现,而无需改动业务逻辑或者其他部分的代码。

而这正是清洁架构的一个重要原则:独立性和隔离变化,即依赖抽象而不是具体实现。

基于此设计如下的存储器接口

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):Booleanfun 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接口设计的目的是为了隐藏实现细节和提高代码的可读性、可维护性和可扩展性。

下面我们基于此继续扩展我们的Storage功能

首先,我们的数据我们希望是序列化存储的,并且可以支持加密。

因此我们继续定义接口:


interface Serializer {fun serialize(obj: Any): Stringfun <T> deserialize(obj: String, classOfT: Class<T>): Tfun <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): Stringfun decrypt(obj: String): String
}

接下来我们就可以使用这两个接口来执行序列化、反序列化,加密和解密的操作。

首先MMKV是支持加密的,但是MMKV使用的是AES CFB-128加密算法来做的。但是它并不是那么足够安全,它没有提供硬件级别的安全加密方法。所以可以考虑自己使用Android KeyStore 来实现硬件级别的加密。

使用Android Keystore来实现,一般大致思路就是拿使用Android的keystore 创建一组加密对密钥,然后使用AES算法来加密和解密。

序列化我们可以使用ProtoBuf或者是json来实现

下面简单使用gson来实现我们的序列化存储如下:

@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)}
}

定义好了接口,实现起来就很简单了,只需要在修改key-value的时候,发送一个key被修改的消息到一个flow,对flow的订阅者就可以订阅数据的改变了。

接下来我们基于MMKV和SharePreference来实现这个存储接口

首先我们来使用SharePreference和MMKV来实现这个存储功能
 

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.keysallKeys.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的实现:

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}"}}

通过上面的代码,我们就可以实现订阅数据的改变。

http://www.hkea.cn/news/700566/

相关文章:

  • 网站建设开发成本天津百度搜索网站排名
  • 做的好的营销型网站有哪些内容外贸网站外链平台
  • 东营网站建设预算价格百度网盘网页版入口官网
  • 网站建设中标公告18款禁用看奶app入口
  • 网站运营人员岗位职责长沙正规seo优化价格
  • cnzz统计代码放在后台网站为什么没显示seo的英文全称是什么
  • 杭州企业网站建设方案广告门
  • 自己免费做网站(二)seo优化公司信
  • 广州外贸b2b网站建设刷钻业务推广网站
  • 做企业网站用什么怎样宣传自己的品牌
  • 濮阳做网站的公司我的百度账号
  • 美食网站开发如何做好网络营销工作
  • 网站建设案例资料今天的新闻内容
  • 台州专业网站建设方案seo软文代写
  • 个人网站 百度推广全球搜索大全
  • 网站消息推送5118素材网站
  • 天津 响应式网站设计企业网站模板免费
  • 网站用花生壳nas做存储百度seo发包工具
  • wordpress cache深圳纯手工seo
  • 怎样找到正规代加工网站百度地图3d实景地图
  • 潍坊网站建设公司网站搭建免费
  • 惠州做网站好的公司下载百度语音导航地图安装
  • 春节网站怎么做小说排行榜百度搜索风云榜
  • 商城服务是什么软件seo是指什么岗位
  • 无锡网站建设有限公司网站快速收录的方法
  • 网站建设通报推广网站多少钱
  • 网络推广公司成都seo排名优化教程
  • 一台手机登录微信网页版西安优化外
  • 如何做旅游攻略网站长沙seo优化推荐
  • 长春火车站电话咨询电话快排seo