- KV存储
- 缓存
- 移动开发
- 存储
【免费下载链接】MMKV
An efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, POSIX, and OHOS.
导读
mmkv_platform_interface是 MMKV 官方 Flutter 插件(flutter/mmkv)所依赖的公共平台接口包:它把 MMKV 的全部原生能力(初始化、编解码、进程间同步、加密、备份恢复等)抽象为统一的 Dart 接口,让 Android、iOS、Linux、Windows、OHOS 等各平台实现遵循同一套契约。本文以 flutter/mmkv_platform_interface/README.md 为骨架,结合 接口源码、FFI 辅助类 以及各平台真实实现,完整讲解平台接口的架构、如何扩展自定义平台实现、接口包含的能力全集,以及 Flutter 官方推荐的“非破坏性变更”演进策略,帮助你理解并二次开发 MMKV 的 Flutter 绑定。
一、平台接口是什么:为什么 MMKV Flutter 插件需要它
Flutter 插件生态中有一个成熟的架构约定:主插件包与平台实现解耦,中间通过一个独立的“平台接口(platform interface)”包对齐契约。mmkv_platform_interface就是这个契约层:
- 主包
flutter/mmkv只面向业务开发者,提供MMKV、NameSpace、MMKVHandler等易用 API; - 平台接口包定义抽象的
MMKVPluginPlatform,声明了与底层原生 MMKV 一一对应的函数签名; - 各平台包(
flutter/mmkv_ios、flutter/mmkv_android、flutter/mmkv_linux、flutter/mmkv_win32、flutter/mmkv_ohos)继承该接口并给出各自的原生实现。
正如 README 所述,这一接口的作用是:“允许平台特定实现与插件本身确保它们支持同一套接口”。从源码可以印证这种解耦的彻底程度——主插件包 mmkv.dart 中,所有底层调用都不是直接调用具体平台代码,而是先通过MMKVPluginPlatform.instance获取当前注册的实现,再调用其暴露的函数(例如_getMMKVWithID = _mmkvPlatform.getMMKVWithIDFunc()),也就是说业务层与平台层唯一的联系就是MMKVPluginPlatform.instance这个静态实例。
二、核心抽象:MMKVPluginPlatform 与 MMKVPluginPlatformFFI
2.1 抽象基类 MMKVPluginPlatform
MMKVPluginPlatform是所有 MMKV 平台插件实现必须继承的抽象基类,其关键设计点如下:
- 持有静态实例
static MMKVPluginPlatform? instance = null,平台包在注册时通过MMKVPluginPlatform.instance = MyMMKVPluginPlatform()注入默认实现; - 持有
MMKVHandler? theHandler,用于承载日志重定向、CRC 校验失败恢复策略、进程间变更通知等回调; - 每个方法默认
throw UnimplementedError(),即“平台必须实现,否则调用即抛错”; - 提供
getApplicationDocumentsPath()与getTemporaryPath()两个可覆写点,默认基于path_provider实现,因为“有些平台并未在 pub.dev 发布自己的 path_provider 包”(见源码注释)。
2.2 FFI 辅助类 MMKVPluginPlatformFFI
为了让 FFI 型平台实现免于重复编写底层查找逻辑,仓库提供了 MMKVPluginPlatformFFI:
DynamicLibrary nativeLib():告诉框架去哪个动态库查找符号,默认抛UnimplementedError,由具体平台覆写;String nativeFuncName(String name):提供一个“映射原生函数名”的机会,用于避免符号冲突,默认原样返回;- 它基于
dart:ffi的DynamicLibrary.lookup<NativeFunction<...>>().asFunction()模式,为接口中绝大部分方法实现了符号查找逻辑,例如getMMKVWithIDFunc()查找 C 函数getMMKVWithID、encodeBoolV2Func()查找encodeBool_v2等; freePtrFunc()带有保护逻辑:若原生库未导出freePtr(早期版本不支持),则回退到calloc.free(见 CHANGELOG v2.2.3 “Protect from freePtr() not found”)。
因此,一个典型的 FFI 平台实现只需要覆写nativeLib()(以及按需覆写nativeFuncName()和initialize()),即可获得整套 MMKV 能力的绑定。这也解释了为什么 README 建议自定义平台实现时去参考mmkv_ios或mmkv_android——它们都是继承MMKVPluginPlatformFFI的最小化示例。
三、实战:如何实现并注册一个新的 MMKV 平台实现
README 给出的使用方法是本文的核心实操内容,完整流程如下:
3.1 继承接口并实现平台行为
import 'package:mmkv_platform_interface/mmkv_platform_interface.dart'; class MyMMKVPluginPlatform extends MMKVPluginPlatform { // 在这里实现平台相关的具体行为 @override Future<String> initialize(String rootDir, {String? groupDir, int logLevel = 1, ...}) async { // 调用原生 MMKV 初始化,返回实际的 rootDir return rootDir; } // 其余方法(encode/decode/reKey/...)按需覆写 }如果是基于 FFI(动态库符号)的实现,更推荐继承MMKVPluginPlatformFFI,只需覆写动态库来源:
class MyMMKVPluginPlatform extends MMKVPluginPlatformFFI { @override DynamicLibrary nativeLib() { return DynamicLibrary.open("libmmkv.so"); } @override String nativeFuncName(String name) { return "my_prefix_$name"; // 按需避免符号冲突 } }3.2 注册为默认实现
在插件注册时设置默认平台实例:
void registerWith() { MMKVPluginPlatform.instance = MyMMKVPluginPlatform(); }这一步是 README 强调的关键动作:只有设置了instance,主插件包flutter/mmkv中的MMKV.initialize()、MMKV(mmapID)等 API 才能真正工作,因为 mmkv.dart 中final MMKVPluginPlatform _mmkvPlatform = MMKVPluginPlatform.instance!;在库加载时就会读取该实例并抓取全部函数指针。
3.3 参考官方示例实现
README 明确指出可参考两个官方实现作为范本:
- flutter/mmkv_ios/lib/mmkv_ios.dart:
MMKVPlatformIOS通过DynamicLibrary.process()获取宿主进程已加载的动态库,并把所有原生函数名统一加上mmkv_前缀(覆写nativeFuncName返回"mmkv_$name"),初始化时调用mmkvInitialize; - flutter/mmkv_android/lib/mmkv_android.dart:
MMKVPlatformAndroid通过DynamicLibrary.open("libmmkv.so")加载 JNI 侧编出的共享库,初始化时额外通过MethodChannel("mmkv")查询getSdkVersion、并用getTemporaryPath()获取缓存目录后调用mmkvInitialize_v2。
此外,仓库还提供了更多同构实现佐证这一模式的普适性:
- flutter/mmkv_linux/lib/mmkv_linux.dart 加载
libmmkv_linux_plugin.so,调用mmkvInitialize; - flutter/mmkv_win32/lib/mmkv_win32.dart 加载
mmkv_win32_plugin.dll,初始化返回Pointer<Utf16>(Windows 宽字符路径); - flutter/mmkv_ohos/lib/mmkv_ohos.dart 则走
MethodChannel("mmkv")的initializeMMKV调用,并覆写了getApplicationDocumentsPath()/getTemporaryPath()两个 path_provider 缺口的场景,对应了 2.1 节提到的“某些平台未发布 path_provider 包”的设计动机。
四、接口能力全景:从初始化到备份恢复
虽然 README 未逐一列举,但接口源码中约 70 个函数签名构成了 MMKV 完整的能力矩阵。按功能归类如下,便于二次实现时对照:
4.1 初始化与实例管理
| 接口方法 | 对应原生能力 |
|---|---|
initialize(rootDir, groupDir, logLevel, logHandler) | 全局初始化,返回实际根目录(如 iOS App Group 目录) |
getMMKVWithIDFunc() | 按mmapID创建/获取实例(含 mode、cryptKey、rootDir、expectedCapacity、namespace、aes256、过期、compare-before-set、恢复策略、itemSizeLimit 等参数) |
getDefaultMMKVFunc() | 获取通用默认实例 |
mmapIDFunc() | 查询实例的 ID |
mmkvCloseFunc() | 永久关闭底层原生实例 |
removeStorageFunc() | 删除数据文件与.crc元文件 |
checkExistFunc()/isFileValidFunc() | 检查实例存在性与文件有效性 |
4.2 数据编解码(含 V2 过期版本)
Bool、Int32、Int64、Double、Bytes 各有一套encode*/decode*,其中*V2Func()变体额外接收expiredInSeconds参数,用于单 key 级过期(见 mmkv.dart 的 encodeBool/encodeInt32 等实现中expireDurationInSecond可选参数)。此外还有:
valueSizeFunc():查询 key 值实际占用大小;writeValueToNBFunc():写入预分配的原生缓冲区;allKeysFunc()/containsKeyFunc()/countFunc():遍历与查询;removeValueForKeyFunc()/removeValuesForKeysFunc()/clearAllFunc():删除能力。
4.3 加密与安全
reKeyFunc()/cryptKeyFunc()/checkReSetCryptKeyFunc():重设密钥、查询密钥、多进程场景下仅重置密钥不加密(均支持 AES-256,见 CHANGELOG v2.3.0);- 业务侧约束:
cryptKey最多 16 字节(见 mmkv.dart 中MMKV构造注释)。
4.4 进程间同步与回调注册
registerErrorHandlerFunc():注册错误回调(CRC 校验失败、文件长度错误),对应MMKVRecoverStrategic恢复策略;registerContentHandlerFunc()/registerContentLoadedHandlerFunc():注册进程间内容变更通知与加载完成通知;checkContentChangedFunc():手动检查其他进程是否修改了内容;isMultiProcessFunc()/isReadOnlyFunc():查询实例模式。
4.5 备份恢复与其他工具
backupOneFunc()/restoreOneFunc()/backupAllFunc()/restoreAllFunc():单实例与全量备份恢复(CHANGELOG v2.2.0 起逐步引入);importFromFunc():从另一个 MMKV 实例导入全部键值(v2.2.1 新增);enableAutoExpireFunc()/disableAutoExpireFunc()、enableCompareBeforeSetFunc()/disableCompareBeforeSetFunc():自动过期与写前比较开关;trimFunc()/clearMemoryCacheFunc()/mmkvSyncFunc():文件瘦身、内存缓存清理、手动同步;pageSizeFunc()/versionFunc()/groupPathFunc():系统信息查询(groupPath仅 iOS 多进程 App Group 场景,非 Darwin 平台返回 null,见 mmkv.dart);getNameSpaceFunc():校验自定义根目录路径是否有效(对应MMKV.nameSpace(path));memcpyFunc()/freePtrFunc():内存拷贝与指针释放(Windows 场景尤为重要,见 CHANGELOG v2.2.2)。
五、回调契约:MMKVHandler、日志级别与恢复策略
接口包还定义了业务方可覆写的回调模型,这些类型也被主包export出去直接暴露给业务开发者:
5.1 MMKVLogLevel 与 MMKVRecoverStrategic
enum MMKVLogLevel { Debug, Info, Warning, Error, None } enum MMKVRecoverStrategic { OnErrorDiscard, OnErrorRecover }MMKVLogLevel对应原生日志级别,MMKV.initialize(logLevel: MMKVLogLevel.Info)默认 Info;MMKVRecoverStrategic.OnErrorDiscard为默认策略:CRC 校验失败或文件长度错误时丢弃全部数据;OnErrorRecover则尽可能恢复数据。
5.2 MMKVHandler 回调集
MMKVHandler定义了一组带默认行为的虚回调:
| 回调 | 默认行为 | 用途 |
|---|---|---|
wantLogRedirect() | false | 是否开启日志重定向 |
mmkvLog(level, file, line, function, message) | print 重定向日志 | 自定义日志输出 |
onMMKVCRCCheckFail(mmapID) | OnErrorDiscard | CRC 校验失败恢复策略 |
onMMKVFileLengthError(mmapID) | OnErrorDiscard | 文件长度错误恢复策略 |
wantContentChangeNotification() | false | 是否启用进程间变更通知 |
onContentChangedByOuterProcess(mmapID) | 空实现 | 其他进程修改内容时回调 |
onMMKVContentLoadSuccessfully(mmapID) | 空实现 | 文件加载成功回调(v2.4.0 新增) |
从 mmkv.dart 的初始化逻辑可以看到回调如何被桥接到原生:若handler.wantLogRedirect()返回 true,则通过Pointer.fromFunction<LogCallbackWrap>(_logRedirect)注册日志回调;_errorHandler将原生错误类型映射为_MMKVErrorType(MMKVCRCCheckFail / MMKVFileLength)并转调onMMKVCRCCheckFail/onMMKVFileLengthError;内容变更与加载完成通知同样以原生回调方式注册。
六、版本演进与破坏性变更策略
README 特别强调:“本包强烈倾向于非破坏性变更(如给接口新增一个方法),而不是破坏性变更”,并引用了 Flutter 官方关于“为什么一个不那么干净的接口优于破坏性变更”的讨论(flutter.dev/go/platform-interface-breaking-changes)。这一策略在 CHANGELOG.md 中有清晰体现:
- v1.0.0(2024-04-19)初始发布;
- 后续功能几乎都以“新增方法”方式平滑演进:v1.0.1 增加 path_provider 覆写点、v2.0.0/v2.1.0 才因
getNameSpace()等引入刻意为之的 2.x 版本(“Bump to setup a breaking change version”)、v2.2.x 增加freePtr/importFrom、v2.3.0 支持 AES-256、v2.4.0 增加MMKVConfig与defaultMMKV(config)支持及onMMKVContentLoadSuccessfully; - 即使需要变更,也尽量通过新增方法或新增参数完成,避免既有平台实现全部失效。
对于维护自定义平台的开发者,这意味着一套兼容原则:向接口新增方法时,应尽量为它提供默认实现或至少不删除旧方法;升级平台接口版本时,检查 CHANGELOG 中标注的 breaking change 版本(如 v2.0.0、v2.1.0)。
七、在 Flutter 项目中使用与二次开发建议
7.1 引入方式
在pubspec.yaml中声明依赖即可(接口包本身依赖flutter、ffi、path_provider,见 pubspec.yaml):
dependencies: mmkv_platform_interface: ^2.4.0业务侧通常不需要直接依赖该包——flutter/mmkv主包已export了MMKVHandler、MMKVLogLevel、MMKVRecoverStrategic等类型(见 mmkv.dart 的 export 语句),业务代码直接使用主包 API 即可。
7.2 二次开发清单
若你要为某个新平台(或自有引擎)接入 MMKV:
- 继承
MMKVPluginPlatform(FFI 场景继承MMKVPluginPlatformFFI),逐项覆写 4.x 小节列出的函数; - 覆写
initialize(),返回真实的 rootDir(参考 Android/iOS/Linux/Windows/OHOS 五个官方实现,注意 Windows 返回Utf16、OHOS 走 MethodChannel 的差异); - 若目标平台没有 path_provider 支持,覆写
getApplicationDocumentsPath()/getTemporaryPath(); - 在插件注册时执行
MMKVPluginPlatform.instance = YourPlatform();; - 变更接口时遵循“非破坏性优先”原则,并同步更新 CHANGELOG 与版本号。
7.3 进一步阅读
- 接口完整定义:lib/mmkv_platform_interface.dart
- FFI 绑定辅助类:lib/mmkv_platform_ffi.dart
- 主插件消费方:flutter/mmkv/lib/mmkv.dart
- 版本演进记录:flutter/mmkv_platform_interface/CHANGELOG.md
- 示例实现:Android(mmkv_android)、iOS(mmkv_ios)、Linux(mmkv_linux)、Windows(mmkv_win32)、OHOS(mmkv_ohos)
总结
mmkv_platform_interface遵循 Flutter 官方插件架构的最佳实践,用一套统一抽象把 MMKV 原生能力完整映射到 Dart 世界:业务层只面对MMKVPluginPlatform.instance,平台层只需继承接口(或 FFI 辅助类)并注册实例。本文覆盖了从抽象基类设计、FFI 辅助、五个官方平台实现、完整能力矩阵到回调契约与版本演进策略的全部要点,无论是理解 MMKV Flutter 插件的工作原理,还是为新的平台接入自定义实现,都可以以此作为直接的代码与设计参考。
- KV存储
- 缓存
- 移动开发
- 存储
【免费下载链接】MMKV
An efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, POSIX, and OHOS.
相关推荐
webview_flutter_platform_interface 深入解析:Flutter WebView 插件平台接口设计与自定义实现指南
webview_flutter_platform_interface 深入解析:Flutter WebView 插件平台接口设计与自定义实现指南 webview
移动开发跨平台path_provider_platform_interface 深入解析:Flutter 路径提供插件的统一平台接口设计
path_provider_platform_interface 深入解析:Flutter 路径提供插件的统一平台接口设计 path_provider_plat
移动开发跨平台Flutter 官方插件平台接口解析:shared_preferences_platform_interface 的设计与实现
Flutter 官方插件平台接口解析:shared_preferences_platform_interface 的设计与实现 导读 shared_prefer
移动开发跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考