非常好用的Android開發(fā)key-value數(shù)據(jù)緩存工具-kvcache,和SharedPreference代碼說byebye

kvcache

歡迎Star??????Github:kvcache:在Android開發(fā)中優(yōu)雅的存取key/value數(shù)據(jù),從此不用再寫SharedPreference代碼

kvcache 簡介

該庫可幫助你在Andrtoid開發(fā)中以更好的方式處理key-value數(shù)據(jù)。從現(xiàn)在開始,將您的sharedpreference代碼和其他鍵值代碼可以更改為kvcache,并編寫更簡介更漂亮的代碼。

如何使用

步驟1/2/3是基本的依賴和初始化,很簡單
當你需要緩存一個鍵值對的時候,只需按照步驟4寫一個方法即可
然后就可以像步驟5那樣使用了

Step 1. 在項目的根 build.gradle 里添加maven庫(如果已有則不需要了):

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Step 2. 在app的build.gradle中添加依賴:

dependencies {
        implementation 'com.github.mazouri:kvcache:1.1'
}

Step 3. 可以在Application中初始化KVCache,并得到全局的對象kv:

private fun initKV() {
    val kvCache = KVCache.Builder(this)
        // 使用addServiceConfig()設(shè)置您自己的緩存方法
        // 如果未設(shè)置,則默認使用SharedPreference
        // Use addServiceConfig() to set your own storage method
        // If you not set, shared preference will be used by default
        .addServiceConfig(SimpleIKVConfig())
        // 如果使用默認的SharedPreference方式來緩存,則文件名默認為kv_cache_shared_prefs.xml
        // 可以使用addDefaultPrefsFileName()來修改保存的文件名
        // If the default shared preference storage is used, the file name defaults to kv_cache_shared_prefs.xml
        // You can use addDefaultPrefsFileName() to modify the saved file name
        .addDefaultPrefsFileName("kv_cache_main_sp")
        .build()

    kv = kvCache.create(KV::class.java)
}

Step 4. 創(chuàng)建一個接口KV用于配置你的key-value信息:

  • @KEY:對的,就是key的值
  • @DEFAULT:默認value,都是字符串(放心使用吧)
  • Call<Type>:Type按照你的需要定義基本數(shù)據(jù)類型
    interface KV {

    @KEY("save_String")
    @DEFAULT("")
    fun testKVCacheString(): Call<String>

    @KEY("save_int")
    @DEFAULT("0")
    fun testKVCacheInt(): Call<Int>

    @KEY("save_boolean")
    @DEFAULT("false")
    fun testKVCacheBoolean(): Call<Boolean>

    @KEY("save_float")
    @DEFAULT("0")
    fun testKVCacheFloat(): Call<Float>

    @KEY("save_long")
    @DEFAULT("0")
    fun testKVCacheLong(): Call<Long>
}

Step 5.然后,你就可以在項目的任何地方簡單方便的使用了:

    // save you key value to cache, default is using shared preference
    KVApp.kv.testKVCacheString().put("hello KVCache")
    KVApp.kv.testKVCacheInt().put(2020)
    KVApp.kv.testKVCacheFloat().put(3.14f)
    KVApp.kv.testKVCacheBoolean().put(true)
    KVApp.kv.testKVCacheLong().put(111100001111L)

    // get value by key
    val resultString = KVApp.kv.testKVCacheString().get()
    val resultInt = KVApp.kv.testKVCacheInt().get()
    val resultFloat = KVApp.kv.testKVCacheFloat().get()
    val resultBoolean = KVApp.kv.testKVCacheBoolean().get()
    val resultLong = KVApp.kv.testKVCacheLong().get()

kvcache 的實現(xiàn)原理

如果你使用過Retrofit,并且理解Retrofit的實現(xiàn)原理,那么你看下面的實現(xiàn)原理將非常順滑

KVCache

KVCache使用設(shè)計模式中的門面模式,客戶端僅需使用該類

KVCache的創(chuàng)建方式如下:

private fun initKV() {
        val kvCache = KVCache.Builder(this)
            .addServiceConfig(SimpleIKVConfig())
            .addDefaultPrefsFileName("kv_cache_main_sp")
            .build()

        kv = kvCache.create(KV::class.java)
    }

使用了設(shè)計模式中的Builder模式

Builder代碼如下:

class KVCache private constructor(private val builder: Builder){
    
    ...
    
    class Builder constructor(val context: Context) {
        internal var serviceConfig: IKVConfig? = null
        internal var filename: String = ""

        fun addServiceConfig(serviceConfig: IKVConfig): Builder {
            this.serviceConfig = serviceConfig
            return this
        }

        fun addDefaultPrefsFileName(filename: String): Builder {
            this.filename = filename
            return this
        }

        fun build(): KVCache {
            return KVCache(this)
        }
    }
}

通過該類,可以配置自定義的ServiceConfig和默認緩存方式shared_preference使用的文件名。

下面看KVCache的create函數(shù)實現(xiàn):

    @Suppress("UNCHECKED_CAST")
    fun <T> create(service: Class<T>): T {
        context = builder.context.applicationContext
        val proxy = Proxy.newProxyInstance(
            service.classLoader,
            arrayOf<Class<*>>(service),
            object : InvocationHandler {
                override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
                    if (method!!.declaringClass == Any::class.java) {
                        return method.invoke(this, args)
                    }
                    return KVCall<T>(loadServiceMethod(method))
                }

            }
        )

        return proxy as T
    }

使用了設(shè)計模式中的動態(tài)代理模式

通過動態(tài)代理,將客戶端寫的KV接口的類,轉(zhuǎn)換成KV對象調(diào)用請求

這里看下loadServiceMethod(method)的實現(xiàn):

    private fun loadServiceMethod(method: Method): KVMethod<*> {
        var result = serviceMethodCache[method]
        if (result != null) return result

        // 2.解析method
        synchronized(serviceMethodCache) {
            result = serviceMethodCache[method]

            if (result == null) {
                // 3.將method傳到KVMethod中解析
                result = KVMethod.Builder<Any?>(method).build()
                serviceMethodCache[method] = result as KVMethod<*>
            }
        }

        return result!!
    }

這里加了一個緩存,如果之前已經(jīng)解析過的方法,就不用再解析了,提高效率。

然后將method傳到KVMethod中去解析

KVMethod

該類用于解析客戶端定義的注解類

從上面的代碼可以知道KVMethod也是通過Builder構(gòu)建的

這里使用了設(shè)計模式的Builder模式

重點我們看下,method傳進來后,是怎么解析的:

class Builder<T> constructor(private val method: Method) {

        private val methodAnnotations = method.annotations
        var typeClass: Class<T>
        lateinit var key: String
        lateinit var default: String
        var isSync = false

        init {
            val returnType = method.genericReturnType as ParameterizedType
            typeClass = returnType.actualTypeArguments[0] as Class<T>
        }

        fun build(): KVMethod<T> {
            for (annotation in methodAnnotations) {
                parseMethodAnnotation(annotation)
            }

            return KVMethod<T>(this)
        }

        private fun parseMethodAnnotation(annotation: Annotation) {
            when(annotation) {
                is KEY -> key = annotation.value
                is DEFAULT -> default = annotation.value
                is SYNC -> isSync = true
            }
        }
    }

拿到method的注解信息和返回值類型。

比如下面的代碼:

        @KEY("save_String")
        @DEFAULT("")
        fun testKVCacheString(): Call<String>

解析注解時,

  • 如果發(fā)現(xiàn)注解是KEY這個類型,就把該注解的value值給到key
  • 如果發(fā)現(xiàn)注解是DEFAULT這個類型,就把該注解的value值給到default

KVCall

在存取數(shù)據(jù)的時候會這么使用:

        // save you key value to cache, default is using shared preference
        KVApp.kv.testKVCacheString().put("hello KVCache")
       
        // get value by key
        val resultString = KVApp.kv.testKVCacheString().get()

那么put()和get()是怎么實現(xiàn)的呢?

看下KVCall這個類中的put方法:

internal class KVCall<T> constructor(private val serviceMethod: KVMethod<*>): Call<T> {

    ...
    
    override fun put(value: T) {
            val key = serviceMethod.key + key
            val cls = serviceMethod.typeClass
            val isSync = serviceMethod.isSync
    
            try {
                when (cls) {
                    java.lang.Integer::class.java -> {
                        setIntValue(key, value as Int, isSync)
                    }
                    java.lang.Float::class.java -> {
                        setFloatValue(key, value as Float, isSync)
                    }
                    java.lang.Boolean::class.java -> {
                        setBooleanValue(key, value as Boolean, isSync)
                    }
                    java.lang.Long::class.java -> {
                        setLongValue(key, value as Long, isSync)
                    }
                    java.lang.String::class.java -> {
                        setStringValue(key, value as String, isSync)
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        
        ...
        
        private fun setStringValue(key: String, value: String, isSync: Boolean) {
            KVConfigManager.instance.setStringValue(key, value, isSync)
       }
}

在這里,根據(jù)KVMethod的返回值類型,調(diào)用緩存管理類KVConfigManager設(shè)置對應(yīng)的數(shù)據(jù)

IKVConfig

緩存key-value數(shù)據(jù)的接口類,客戶端通過實現(xiàn)該接口實現(xiàn)自己的緩存方式

interface IKVConfig {
    fun hasKey(key: String?): Boolean

    fun getLongValue(key: String?, defValue: Long): Long

    fun getBooleanValue(key: String?, defValue: Boolean): Boolean

    fun getIntValue(key: String?, defValue: Int): Int

    fun getFloatValue(key: String?, defValue: Float): Float

    fun getStringValue(key: String?, defValue: String?): String?

    fun setBooleanValue(
        key: String?,
        value: Boolean,
        isSync: Boolean
    )

    fun setLongValue(
        key: String?,
        value: Long,
        isSync: Boolean
    )

    fun setIntValue(
        key: String?,
        value: Int,
        isSync: Boolean)

    fun setFloatValue(
        key: String?,
        value: Float,
        isSync: Boolean
    )

    fun setStringValue(
        key: String?,
        value: String?,
        isSync: Boolean
    )
}

KVConfigManager

緩存方式管理類,默認使用KVPrefs

internal class KVConfigManager private constructor(): IKVConfig {

    private var serviceConfig: IKVConfig = KVPrefs()

    companion object {
        val instance: KVConfigManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            KVConfigManager()
        }
    }

    internal fun setServiceConfig(serviceConfig: IKVConfig?) {
        Log.d("", "setServiceConfig called")
        if (serviceConfig == null) return
        this.serviceConfig = serviceConfig
    }
    
    override fun setStringValue(key: String?, value: String?, isSync: Boolean) = serviceConfig.setStringValue(key, value, isSync)
    ...
} 

這里使用了設(shè)計模式中的單例模式

如果客戶端沒有使用自定義的緩存方式,則默認使用KVPrefs緩存

KVPrefs

默認的存儲方式,即使用SharedPreferences

比較簡單,這里就不贅述了,可以直接看源碼KVPrefs

歡迎Star??????Github:kvcache:在Android開發(fā)中優(yōu)雅的存取key/value數(shù)據(jù),從此不用再寫SharedPreference代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容