Android-性能优化-07- 存储优化-MMKV-使用 kotlin封装

6 阅读3分钟

kotlin的属性委托大家应该比较了解,主要用于属性的存取,这里我就不做过多介绍了,下面就直接贴脸开大,哦不,是直接贴代码了。

kv帮助类

首先需要一个key-value的帮助类,如下:

kotlin
代码解读
复制代码
object KVUtils {

    // 使用延迟委托创建MMKV默认的实例
    private val mmkv: MMKV by lazy(LazyThreadSafetyMode.NONE) {
        MMKV.defaultMMKV()
    }

    fun <T> encode(localMMKV: MMKV?, key: String, value: T) {
        val kv = localMMKV ?: mmkv
        when (value) {
            is Int -> kv.encode(key, value)
            is Long -> kv.encode(key, value)
            is Float -> kv.encode(key, value)
            is Double -> kv.encode(key, value)
            is Boolean -> kv.encode(key, value)
            is String -> kv.encode(key, value)
        }
    }

    @Suppress("UNCHECKED_CAST")
    fun <T> decode(localMMKV: MMKV?, key: String, defValue: T): T {
        val kv = localMMKV ?: mmkv
        return when (defValue) {
            is Int -> kv.decodeInt(key, defValue) as T
            is Long -> kv.decodeLong(key, defValue) as T
            is Float -> kv.decodeFloat(key, defValue) as T
            is Double -> kv.decodeDouble(key, defValue) as T
            is Boolean -> kv.decodeBool(key, defValue) as T
            is String -> kv.decodeString(key, defValue) as T
            else -> defValue
        }
    }

}

注意这里的encode和decode方法,里面有一个可以为空的MMKV的参数localMMKV,该参数通过委托对象传入,具体是干什么用的,后面再做介绍,先把代码贴完就知道是怎么用的了。

委托类

kotlin
代码解读
复制代码
class KVDelegate<T>(private val def: T) : ReadWriteProperty<Any?, T> {

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return if (thisRef is KVOwner) {
            KVUtils.decode(thisRef.mmkv, property.name, def)
        } else {
            KVUtils.decode(null, property.name, def)
        }
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        if (thisRef is KVOwner) {
            KVUtils.encode(thisRef.mmkv, property.name, value)
        } else {
            KVUtils.encode(null, property.name, value)
        }
    }
}

这里因为需要对属性有读写操作,所以需要继承ReadWriteProperty 并实现setValue和getValue方法

另外里面还有一个不认识的KVOwner,这个就很简单了,就是一个接口而已。

kotlin
代码解读
复制代码
interface KVOwner {
    val mmkv: MMKV
}

但是该接口非常重要,它代表了我们之前写的KV的帮助类里面的localMMKV。

好,以上就是使用Kotlin属性委托对MMKV组件的全部封装了,那怎么使用呢?

使用委托

假如项目中有一个用户模块,该模块需要存储用户的名称,我们定义一个UserRepository, 然后:

csharp
代码解读
复制代码
object UserRepository {
    var userName: String by KVDelegate("")
}

写入用户名称,比如张三:
UserRepository.userName = "张三"

读取用户名称
val userName = UserRepository.userName

没错,就是这么简洁,连key都不用写。

注意,以上委托默认使用的kv帮助类里面的默认的mmkv实例,即,userName该变量是存储在MMKV组件默认的文件中的。

那项目中有另外一个模块,比如书籍模块,该模块要存入书籍的编号,但是不想存在MMKV组件的默认文件中,即该书籍编号和用户模块的用户名称不存在中一个文件中,那要怎么处理呢? 我们可以定义一个BookRepository

kotlin
代码解读
复制代码
object BookRepository : KVOwner {

    override val mmkv: MMKV
        get() = MMKV.mmkvWithID("book")

    var bookNo: Long by KVDelegate("")
}

注意这个时候BookRepository的写法,它继承自KVOwner,并重写了熟悉mmkv,实例化了一个关于book的mmkv对象,这个时候使用属性委托,bookNo存入的就是book的文件中。

为什么会这样

这就涉及到委托类具体实现了。 在setter和getter方法里面,判断了thisRef是否是KVOwner类型,如果是的话,那么就使用thisRef的mmkv变量。在这个例子中,由于BookRepository实现了KVOwner,thisRef就是BookRepository类型的对象,它在setter和getter方法里调用的是thisRef的mmkv变量就是BookRepository重写的mmkv变量,然后将该变量传入的kv帮助类KVUtils的encode和decode方法中;那想把key-value存在默认的文件中,则不实现KVOwner即可。这样就实现了将key-value存在在不同的文件中。

扩展

如果在viewmodel中,需要将对应的变量暴露给view,然后操作view,通过viewmodel存取这个变量,那要怎么操作呢?直接对变量进行set和get操作就可以了。

arduino
代码解读
复制代码
class MyViewModel(private val repository: Repository) : ViewModel() {
    var userName: String
        get() = repository.userName
        set(value) {
            repository.userName = value
        }
}

但是这种写法不够优雅,这里又可以通过委托简化代码

arduino
代码解读
复制代码
class MyViewModel(private val repository: Repository) : ViewModel() {
    var userName: String by repository::userName
}

对了,直接这样就搞定了。

搞定,打完收工。