正文
MMKV-Kotlin 的总体设计见下图:
四、实现简介
在《
携程机票 App KMM 跨端生产实践
》(参考链接 1)一文的 2.2 小节中我们曾以 MMKV 作为 demo 来介绍 KMM 的 expect-actual 技术。但本次开源的版本为了代码的健壮性与实用性, 调整了具体的实现方式,本节将会进行详细的探讨。
4.1 初始化函数
2.2 小节演示了 MMKV-Kotlin 的初始化,因此其初始化函数是在 Android、iOS 两个 source set 中分别定义与实现的。
先看看 Android:
import android.content.Context
import com.tencent.mmkv.MMKV
fun initialize(context: Context): String = MMKV.initialize(context)
fun initialize(context: Context, rootDir: String): String = MMKV.initialize(context, rootDir)
fun initialize(context: Context, loader: MMKV.LibLoader): String = MMKV.initialize(context, loader)
fun initialize(context: Context, logLevel: MMKVLogLevel): String = MMKV.initialize(context, logLevel.rawValue)
fun initialize(context: Context, rootDir: String, loader: MMKV.LibLoader): String = MMKV.initialize(context, rootDir, loader)
fun initialize(context: Context, rootDir: String, logLevel: MMKVLogLevel): String = MMKV.initialize(context, rootDir, logLevel.rawValue)
fun initialize(context: Context, loader: MMKV.LibLoader, logLevel: MMKVLogLevel): String = MMKV.initialize(context, loader, logLevel.rawValue)
fun initialize(context: Context, rootDir: String, loader: MMKV.LibLoader, logLevel: MMKVLogLevel): String = MMKV.initialize(context, rootDir, loader, logLevel.rawValue)
初始化函数的实现仅仅是调用 MMKV Java API 中的
initialize
函数。Android 平台的初始化强依赖
Context
类型,还提供了
LibLoader
类型作为参数,用于在初始化时加载 so 库。我们希望尽可能满足 Android 平台的各种需求,因此将 MMKV-Android 中的初始化 API 全部暴露出来。
再看看 iOS:
import cocoapods.MMKV.MMKV
fun initialize() = MMKV.initialize()
fun initialize(rootDir: String): String = MMKV.initializeMMKV(rootDir)
fun initialize(rootDir: String, logLevel: MMKVLogLevel): String = MMKV.initializeMMKV(rootDir, logLevel.rawValue)
fun initialize(rootDir: String, groupDir: String, logLevel: MMKVLogLevel): String = MMKV.initializeMMKV(rootDir, groupDir, logLevel.rawValue)
相比之下 iOS 平台少了
Context
类型与
LibLoader
类型,因此初始化函数的重载要少很多。
4.2 MMKV 类型
在 MMKV 的 Java 与 Objective-C 版本中,
MMKV
类型是具体 CRUD 功能的实现类。在 Java 版本中,写函数为一系列
encode
重载函数或统一命名为
putXXX
,其中
putXXX
内部调用了
encode
函数,二者只是返回类型不同,读函数为统一命名为
decodeXXX
或
getXXX
的函数,二者行为一致 。而 Objective-C 版本中,写函数统一命名为
setXXX
函数,读函数统一命名为
getXXX
函数。虽然平台不同,但是具有相同功能的函数的参数数量、类型,以及返回类型都高度统一。因此这给我们定义 common source set 中的
MMKV
类型带来了便利。
我们需要在 common 层声明
MMKV
类型(为避免同名带来的混淆,我们将 common 层的
MMKV
类型命名为
MMKV_KMP
),并且具体实现在各平台的 source set 中,
MMKV
类型的实例需要持有 Java 或 Objective-C 的
MMKV
类型的实例,并将 CURD 操作委托给它们。我们的实现方式有两种可选:
最终我们选择了方案二,原因在于:在平台相关的 source set 中编写的具体实现 class 需要实例化时需要同时构建 Java/Objective-C 的
MMKV
实例,且最好的方式是在其构造函数作为参数传入。而 Java 与 Objective-C 的
MMKV
是两个完全没有任何关系的独立类型,因此我们在 common source set 中统一
MMKV_KMP
的构造函数非常不便。其次,在 MMKV 原本的设计中,
MMKV
的实例本身也不是通过构造函数创建,而是通过一系列工厂方法创建,因此我们没有必要在 common 层定义其构造函数。
确定基本设计后,我们看看
MMKV_KMP
的定义:
interface MMKV_KMP {
operator fun set(key: String, value: String): Boolean
operator fun set(key: String, value: Boolean): Boolean
fun takeString(key: String, default: String = ""): String
fun takeBoolean(key: String, default: Boolean = false): Boolean
fun close