MMKV的基本使用
一、MMKV 是什么 🏪
kotlin
/**
* MMKV就像一个高科技便利店:
* - 比传统商店(SharedPreferences)快10倍
* - 24小时营业(进程存活期间常驻内存)
* - 支持多人同时购物(多进程并发)
*/
class MMKVExplainer {
fun explain() {
// 1. 初始化便利店
MMKV.initialize(context)
// 2. 开一家店
val kv = MMKV.defaultMMKV()
// 3. 存取商品
kv.encode("key", "value") // 上架商品
val value = kv.decodeString("key") // 取商品
}
}
二、工作原理 🔄
graph TD
A[MMKV存储] --> B[内存映射
mmap] A --> C[写入缓存
memory] A --> D[异步持久化
file] B --> E[零拷贝
直接内存访问] C --> F[高速缓存
读写加速] D --> G[数据安全
不丢失] style A fill:#ff9999 style B,C,D fill:#99ff99 style E,F,G fill:#9999ff
mmap] A --> C[写入缓存
memory] A --> D[异步持久化
file] B --> E[零拷贝
直接内存访问] C --> F[高速缓存
读写加速] D --> G[数据安全
不丢失] style A fill:#ff9999 style B,C,D fill:#99ff99 style E,F,G fill:#9999ff
三、与 SharedPreferences 对比 📊
kotlin
/**
* 对比传统便利店(SP)和新型便利店(MMKV)
*/
class StoreComparison {
fun compareStores() {
// 1. 传统便利店 (SharedPreferences)
val sp = context.getSharedPreferences("sp_name", Context.MODE_PRIVATE)
sp.edit()
.putString("key", "value")
.apply() // 等待所有商品上架才开门
// 2. 新型便利店 (MMKV)
val kv = MMKV.defaultMMKV()
kv.encode("key", "value") // 商品即上即用,无需等待
}
}
四、主要特点 🌟
kotlin
/**
* MMKV的超能力
*/
class MMKVFeatures {
fun showFeatures() {
// 1. 极速存取(像闪电快递)
val kv = MMKV.defaultMMKV()
repeat(10000) {
kv.encode("key_$it", "value") // 批量存储也很快
}
// 2. 多进程支持(像连锁店)
val multiKv = MMKV.mmkvWithID("multi_process",
MMKV.MULTI_PROCESS_MODE)
// 3. 加密支持(像保险柜)
val cryptoKv = MMKV.mmkvWithID("encrypted",
null, "your_crypto_key")
}
}
五、使用示例 📱
kotlin
/**
* MMKV实战应用
*/
class MMKVUsage {
// 1. 基础使用
fun basicUsage() {
val kv = MMKV.defaultMMKV()
// 存储各种类型
kv.encode("name", "Tom") // 字符串
kv.encode("age", 18) // 整数
kv.encode("height", 1.75f) // 浮点数
kv.encode("isVIP", true) // 布尔值
kv.encode("data", byteArrayOf()) // 字节数组
// 读取数据
val name = kv.decodeString("name")
val age = kv.decodeInt("age")
}
// 2. 高级用法
fun advancedUsage() {
// 批量操作
val kv = MMKV.defaultMMKV()
// 存储对象(需要序列化)
data class User(val name: String, val age: Int)
val user = User("Tom", 18)
kv.encode("user", Gson().toJson(user))
// 读取对象
val userJson = kv.decodeString("user")
val savedUser = Gson().fromJson(userJson, User::class.java)
}
}
六、性能对比 📊
kotlin
/**
* 性能测试
*/
class PerformanceTest {
fun testPerformance() {
// 1. MMKV写入 (约0.0023ms)
val kv = MMKV.defaultMMKV()
measureTimeMillis {
kv.encode("key", "value")
}
// 2. SP写入 (约0.025ms)
val sp = context.getSharedPreferences("sp", Context.MODE_PRIVATE)
measureTimeMillis {
sp.edit().putString("key", "value").apply()
}
}
}
七、最佳实践 💡
kotlin
/**
* MMKV最佳实践
*/
class MMKVBestPractices {
companion object {
// 1. 单例模式
private val kv by lazy {
MMKV.defaultMMKV()
}
// 2. 封装操作
fun saveUserInfo(user: User) {
kv.encode("user_name", user.name)
kv.encode("user_age", user.age)
}
// 3. 类型安全
fun <T> getValue(key: String, default: T): T {
return when (default) {
is String -> kv.decodeString(key, default) as T
is Int -> kv.decodeInt(key, default) as T
is Boolean -> kv.decodeBool(key, default) as T
else -> throw IllegalArgumentException("Unsupported type")
}
}
}
}
就像一个现代化的便利店:
- 存取速度快(mmap技术)
- 永不关门(内存常驻)
- 多人同时购物(多进程)
- 商品安全(加密支持)
- 即存即取(异步持久化)
使用建议:
- 小数据存储首选
- 需要频繁读写的场景
- 多进程数据共享
- 对性能要求高的场景
这样就能让你的数据存取像便利店一样快捷方便!🏪
更通俗的底层原理
让我用"快递仓库"的例子深入解释 MMKV 的底层原理:
一、核心原理图解 📦
graph TD
A[MMKV存储系统] --> B[内存映射
mmap机制] A --> C[写入缓存
Memory Buffer] A --> D[异步持久化
File] B --> B1[虚拟内存
Virtual Memory] B --> B2[物理内存
Physical Memory] B --> B3[磁盘文件
Disk File] C --> C1[内存对齐
Memory Alignment] C --> C2[增量更新
Incremental Update] D --> D1[写入队列
Write Queue] D --> D2[异步刷盘
Async Flush]
mmap机制] A --> C[写入缓存
Memory Buffer] A --> D[异步持久化
File] B --> B1[虚拟内存
Virtual Memory] B --> B2[物理内存
Physical Memory] B --> B3[磁盘文件
Disk File] C --> C1[内存对齐
Memory Alignment] C --> C2[增量更新
Incremental Update] D --> D1[写入队列
Write Queue] D --> D2[异步刷盘
Async Flush]
二、核心技术解析 🔍
kotlin
/**
* MMKV 核心技术解析
*/
class MMKVPrinciple {
/**
* 1. mmap 内存映射
* 就像快递仓库的智能货架系统:
* - 货物直接从货架取用(零拷贝)
* - 不需要搬运到临时区域(减少拷贝)
*/
fun explainMmap() {
// 原理示意
val fileDescriptor = FileDescriptor()
val mappedByteBuffer = MappedByteBuffer.map(
FileChannel.MapMode.READ_WRITE, // 读写模式
0, // 起始位置
fileSize // 映射大小
)
// 直接操作内存,就像直接操作文件
mappedByteBuffer.putInt(100)
mappedByteBuffer.putString("value")
}
/**
* 2. 写入缓存
* 就像快递分拣区:
* - 先在分拣区处理(内存缓存)
* - 再统一上架(异步写入)
*/
fun explainMemoryBuffer() {
class MemoryBuffer {
private val buffer = ByteBuffer.allocate(8192)
fun write(data: ByteArray) {
// 检查空间
if (buffer.remaining() < data.size) {
// 触发异步写入
asyncWrite()
}
// 写入缓存
buffer.put(data)
}
}
}
/**
* 3. 数据编码
* 就像快递包装优化:
* - 高效的打包方式(protobuf)
* - 节省存储空间
*/
fun explainEncoding() {
// Protocol Buffers 编码示意
data class Item(
val key: String,
val value: ByteArray
)
fun encode(item: Item): ByteArray {
return ProtoBuf.encode(item)
}
}
}
三、性能优化细节 ⚡
kotlin
/**
* MMKV 性能优化细节
*/
class MMKVOptimization {
/**
* 1. 内存对齐
* 就像标准化货架:
* - 固定大小的储位
* - 快速定位取放
*/
fun memoryAlignment() {
// 8字节对齐
fun align8(size: Int): Int {
return (size + 7) and (~7)
}
}
/**
* 2. 增量更新
* 就像货物补货:
* - 只更新变化的部分
* - 减少整体移动
*/
fun incrementalUpdate() {
class UpdateStrategy {
fun update(key: String, value: ByteArray) {
// 1. 找到旧值位置
val oldPosition = findOldValue(key)
// 2. 判断是否需要扩容
if (needExpand(value.size)) {
expandBuffer()
}
// 3. 直接更新数据
updateInPlace(oldPosition, value)
}
}
}
/**
* 3. 异步持久化
* 就像发货系统:
* - 先接收所有订单
* - 批量统一处理
*/
fun asyncPersistence() {
class AsyncWriter {
private val writeQueue = LinkedBlockingQueue<WriteTask>()
fun asyncWrite(data: ByteArray) {
// 加入写入队列
writeQueue.offer(WriteTask(data))
// 触发异步写入
scheduleWrite()
}
}
}
}
四、为什么快?🚀
- 零拷贝技术:
kotlin
/**
* 传统方式 VS MMKV
*/
class CopyComparison {
// 传统方式:需要多次拷贝
fun traditionalWay() {
// 1. 从磁盘读取到内核缓冲区
// 2. 从内核缓冲区拷贝到用户空间
// 3. 处理数据
// 4. 写回内核缓冲区
// 5. 刷入磁盘
}
// MMKV方式:直接操作
fun mmkvWay() {
// 1. 内存映射,直接操作
// 2. 系统自动同步
}
}
- 内存对齐:
kotlin
/**
* 内存访问优化
*/
class MemoryAccess {
// 对齐访问更快
@Aligned(8)
data class AlignedData(
val value: Long // 8字节对齐
)
}
- 增量更新:
kotlin
/**
* 更新策略
*/
class UpdateStrategy {
// 只更新变化的部分
fun updateValue(key: String, value: String) {
// 1. 定位旧值
val oldValue = findValue(key)
// 2. 判断是否需要移动
if (value.length <= oldValue.length) {
// 直接覆盖,不需要移动其他数据
overwriteValue(key, value)
} else {
// 追加到末尾
appendValue(key, value)
}
}
}
五、实际性能对比 📊
kotlin
/**
* 性能测试对比
*/
class PerformanceComparison {
fun comparePerformance() {
// 1. 写入性能
val writeResult = measureTimeMillis {
repeat(10000) {
// MMKV: 0.0023ms/次
// SP: 0.025ms/次
}
}
// 2. 读取性能
val readResult = measureTimeMillis {
repeat(10000) {
// MMKV: 0.0012ms/次
// SP: 0.015ms/次
}
}
}
}
就像一个高科技仓库:
-
智能货架系统(mmap):
- 直接取放,无需搬运
- 系统自动同步
-
高效分拣(内存缓存):
- 批量处理订单
- 减少重复动作
-
优化布局(内存对齐):
- 标准化储位
- 快速存取货物
-
智能补货(增量更新):
- 按需更新库存
- 减少整体移动
这就是为什么 MMKV 能够实现极速存取!🚀
MMKV的跨进程问题
🌟 问题分析
kotlin
// MMKV跨进程同步的基本结构
class MMKVStructure {
/*
关键组件:
1. 共享内存(mmap)
2. 文件锁(FileLock)
3. 内存锁(SharedPreferences)
*/
// 典型崩溃场景
fun typicalCrashCase() {
// 进程A
MMKV.defaultMMKV().encode("key", "value")
// 同时,进程B
MMKV.defaultMMKV().encode("key2", "value2")
// 死锁!
}
}
💡 死锁原因分析
kotlin
// 死锁形成过程
class DeadlockAnalysis {
/*
死锁四要素:
1. 互斥条件:同一时刻只能有一个进程访问共享资源
2. 请求与保持:进程持有资源的同时请求新资源
3. 不可剥夺:资源只能由持有者主动释放
4. 循环等待:多个进程形成等待环
*/
fun deadlockScenario() {
// 进程A
acquireLock(FILE_LOCK) // 获取文件锁
acquireLock(MEMORY_LOCK) // 等待内存锁
// 进程B
acquireLock(MEMORY_LOCK) // 获取内存锁
acquireLock(FILE_LOCK) // 等待文件锁
// 死锁形成!
}
}
⚡ 解决方案
kotlin
// 1. 锁顺序统一
class LockOrderSolution {
fun safeWrite() {
synchronized(GLOBAL_LOCK) {
// 统一加锁顺序
acquireLock(FILE_LOCK)
try {
acquireLock(MEMORY_LOCK)
// 执行写入操作
} finally {
releaseLock(MEMORY_LOCK)
releaseLock(FILE_LOCK)
}
}
}
}
// 2. 超时机制
class TimeoutSolution {
fun writeWithTimeout() {
withTimeout(1000L) {
try {
if (acquireLockWithTimeout(FILE_LOCK)) {
if (acquireLockWithTimeout(MEMORY_LOCK)) {
// 执行写入
}
}
} finally {
releaseAllLocks()
}
}
}
}
// 3. 重试机制
class RetryMechanism {
fun writeWithRetry() {
retry(maxAttempts = 3) {
try {
performSafeWrite()
} catch (e: LockTimeoutException) {
// 等待随机时间后重试
delay(Random.nextLong(100, 500))
throw e
}
}
}
}
🔍 监控与预防
kotlin
// 1. 死锁检测
class DeadlockDetector {
private val lockMonitor = LockMonitor()
fun monitorLocks() {
// 记录锁获取顺序
lockMonitor.recordLockAcquisition(processId, lockId)
// 检测循环依赖
if (lockMonitor.detectDeadlock()) {
// 触发告警
alertDeadlock()
}
}
}
// 2. 性能监控
class PerformanceMonitor {
fun trackLockDuration() {
val startTime = System.nanoTime()
try {
performLockOperation()
} finally {
val duration = System.nanoTime() - startTime
reportLockDuration(duration)
}
}
}
📊 优化建议
kotlin
// 1. 批量写入优化
class BatchWriteOptimization {
fun batchWrite(updates: Map<String, Any>) {
mmkv.withTransaction {
updates.forEach { (key, value) ->
encode(key, value)
}
}
}
}
// 2. 进程通信优化
class IPCOptimization {
// 使用消息队列
val messageQueue = MessageQueue()
fun writeWithQueue() {
messageQueue.send(WriteOperation(key, value))
// 单一进程处理写入
}
}
⚠️ 最佳实践
kotlin
// 完整的解决方案
class MMKVSafeSolution {
companion object {
private const val LOCK_TIMEOUT = 1000L
private const val MAX_RETRY = 3
}
fun safeWrite(key: String, value: Any) {
var attempts = 0
while (attempts++ < MAX_RETRY) {
try {
withTimeout(LOCK_TIMEOUT) {
synchronized(GLOBAL_LOCK) {
// 1. 获取文件锁
acquireLock(FILE_LOCK)
try {
// 2. 获取内存锁
acquireLock(MEMORY_LOCK)
try {
// 3. 执行写入
mmkv.encode(key, value)
} finally {
releaseLock(MEMORY_LOCK)
}
} finally {
releaseLock(FILE_LOCK)
}
}
return // 成功写入
}
} catch (e: TimeoutCancellationException) {
// 超时重试
delay(Random.nextLong(50, 200))
}
}
throw MMKVException("Write failed after $MAX_RETRY attempts")
}
}
🎯 预防措施
- 代码层面:
markdown
- 统一锁的获取顺序
- 实现超时机制
- 添加重试逻辑
- 使用事务机制
- 架构层面:
markdown
- 减少跨进程操作
- 实现批量写入
- 使用消息队列
- 监控锁的状态
记住:
- 统一加锁顺序很重要
- 必须有超时机制
- 要有重试策略
- 监控很关键
如何设计一个支持跨进程事件总线的LiveData
让我详细解释这个 ProcessAwareLiveData 的实现。
🌟 基本原理
kotlin
// ProcessAwareLiveData 利用 MMKV 的跨进程特性实现数据同步
class ProcessAwareLiveData<T>(private val mmkv: MMKV) : LiveData<T>() {
companion object {
const val DATA_KEY = "process_aware_data"
}
/* 工作原理:
1. MMKV 本身支持跨进程共享数据
2. 利用 MMKV 的监听器监听数据变化
3. 当数据变化时更新 LiveData
*/
}
💡 详细实现
kotlin
// 完整实现示例
class ProcessAwareLiveData<T>(
private val mmkv: MMKV,
private val serializer: DataSerializer<T>
) : LiveData<T>() {
// 1. 激活时注册监听
override fun onActive() {
super.onActive()
// 注册 MMKV 变化监听
mmkv.registerOnSharedPreferenceChangeListener { _, key ->
if (key == DATA_KEY) {
// 数据变化时更新 LiveData
val rawData = mmkv.getString(key, "")
val newValue = serializer.deserialize(rawData)
postValue(newValue)
}
}
}
// 2. 发送数据
fun postData(value: T) {
// 序列化数据
val serializedData = serializer.serialize(value)
// 写入 MMKV
mmkv.putString(DATA_KEY, serializedData)
.commit() // 同步写入确保跨进程即时性
}
// 3. 清理资源
override fun onInactive() {
super.onInactive()
// 取消监听
mmkv.unregisterOnSharedPreferenceChangeListener()
}
}
⚡ 使用示例
kotlin
// 1. 创建实例
class EventBusManager {
private val mmkv = MMKV.defaultMMKV()
// 创建支持跨进程的 LiveData
val eventBus = ProcessAwareLiveData<Event>(mmkv, EventSerializer())
}
// 2. 进程A:发送数据
class ProcessA {
fun sendEvent(event: Event) {
eventBus.postData(event)
// MMKV 会将数据写入共享内存
// 其他进程可以立即看到更新
}
}
// 3. 进程B:接收数据
class ProcessB {
fun observeEvents() {
eventBus.observe(lifecycleOwner) { event ->
// 收到跨进程事件
handleEvent(event)
}
}
}
🔄 数据流转图
markdown
进程A MMKV 进程B
| | |
|--- postData() ------>| |
| | |
| |-- 触发监听器 -------->|
| | |
| | |-- postValue()
| | |
| | |-- 更新UI
⚠️ 注意事项
kotlin
// 1. 序列化处理
interface DataSerializer<T> {
fun serialize(data: T): String
fun deserialize(raw: String): T
}
// 2. 性能优化
class OptimizedProcessAwareLiveData<T>(
private val mmkv: MMKV,
private val serializer: DataSerializer<T>
) : LiveData<T>() {
// 添加防抖动
private val debounceJob = Job()
fun postDataWithDebounce(value: T) {
debounceJob.cancel()
CoroutineScope(debounceJob).launch {
delay(100) // 防抖动延迟
postData(value)
}
}
}
// 3. 异常处理
class SafeProcessAwareLiveData<T>(
private val mmkv: MMKV,
private val serializer: DataSerializer<T>
) : LiveData<T>() {
fun postDataSafely(value: T) {
try {
val serialized = serializer.serialize(value)
mmkv.putString(DATA_KEY, serialized)
} catch (e: Exception) {
Log.e("ProcessAwareLiveData", "Error posting data", e)
}
}
}
💡 优缺点分析
优点:
markdown
1. 实现简单,利用 MMKV 现成的跨进程能力
2. 性能好,MMKV 基于 mmap 实现
3. 可靠性高,依赖 MMKV 的稳定性
缺点:
markdown
1. 数据大小受限于 MMKV 限制
2. 需要处理序列化/反序列化
3. 可能存在并发写入问题
🎯 使用建议
kotlin
// 最佳实践
class BestPracticeExample {
// 1. 小数据量使用
val simpleEventBus = ProcessAwareLiveData<SimpleEvent>(mmkv)
// 2. 大数据考虑压缩
val compressedEventBus = ProcessAwareLiveData(
mmkv,
CompressedSerializer()
)
// 3. 高频更新需要防抖
val debouncedEventBus = ProcessAwareLiveData(
mmkv,
DebouncedSerializer()
)
}
记住:
- 适合小数据量的跨进程通信
- 需要注意序列化性能
- 考虑并发和防抖动
- 做好异常处理