kvcache
歡迎Star🌟🌟🌟Github:kvcache:在Android開發中優雅的存取key/value數據,從此不用再寫SharedPreference代碼
kvcache 簡介
該庫可幫助你在Andrtoid開發中以更好的方式處理key-value數據。從現在開始,將您的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()設置您自己的緩存方法
// 如果未設置,則默認使用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. 創建一個接口KV用於配置你的key-value信息:
-
@KEY:對的,就是key的值
-
@DEFAULT:默認value,都是字符串(放心使用吧)
-
Call:Type按照你的需要定義基本數據類型
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
的實現原理
如果你使用過Retrofit,並且理解Retrofit的實現原理,那麼你看下面的實現原理將非常順滑
KVCache
KVCache使用設計模式中的門面模式,客戶端僅需使用該類
KVCache的創建方式如下:
private fun initKV() {
val kvCache = KVCache.Builder(this)
.addServiceConfig(SimpleIKVConfig())
.addDefaultPrefsFileName("kv_cache_main_sp")
.build()
kv = kvCache.create(KV::class.java)
}
使用了設計模式中的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函數實現:
@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
}
使用了設計模式中的動態代理模式
通過動態代理,將客戶端寫的KV接口的類,轉換成KV對象調用請求
這裏看下loadServiceMethod(method)的實現:
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!!
}
這裏加了一個緩存,如果之前已經解析過的方法,就不用再解析了,提高效率。
然後將method傳到KVMethod中去解析
KVMethod
該類用於解析客戶端定義的註解類
從上面的代碼可以知道KVMethod也是通過Builder構建的
這裏使用了設計模式的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>
解析註解時,
- 如果發現註解是KEY這個類型,就把該註解的value值給到key
- 如果發現註解是DEFAULT這個類型,就把該註解的value值給到default
KVCall
在存取數據的時候會這麼使用:
// 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()是怎麼實現的呢?
看下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)
}
}
在這裏,根據KVMethod的返回值類型,調用緩存管理類KVConfigManager設置對應的數據
IKVConfig
緩存key-value數據的接口類,客戶端通過實現該接口實現自己的緩存方式
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)
...
}
這裏使用了設計模式中的單例模式
如果客戶端沒有使用自定義的緩存方式,則默認使用KVPrefs緩存
KVPrefs
默認的存儲方式,即使用SharedPreferences
比較簡單,這裏就不贅述了,可以直接看源碼KVPrefs
歡迎Star🌟🌟🌟Github:kvcache:在Android開發中優雅的存取key/value數據,從此不用再寫SharedPreference代碼