Android - MMKV的基本使用及底层原理

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

三、与 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技术)
  • 永不关门(内存常驻)
  • 多人同时购物(多进程)
  • 商品安全(加密支持)
  • 即存即取(异步持久化)

使用建议:

  1. 小数据存储首选
  2. 需要频繁读写的场景
  3. 多进程数据共享
  4. 对性能要求高的场景

这样就能让你的数据存取像便利店一样快捷方便!🏪

更通俗的底层原理

让我用"快递仓库"的例子深入解释 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]

二、核心技术解析 🔍

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()
            }
        }
    }
}

四、为什么快?🚀

  1. 零拷贝技术:
kotlin 复制代码
/**
 * 传统方式 VS MMKV
 */
class CopyComparison {
    // 传统方式:需要多次拷贝
    fun traditionalWay() {
        // 1. 从磁盘读取到内核缓冲区
        // 2. 从内核缓冲区拷贝到用户空间
        // 3. 处理数据
        // 4. 写回内核缓冲区
        // 5. 刷入磁盘
    }
    
    // MMKV方式:直接操作
    fun mmkvWay() {
        // 1. 内存映射,直接操作
        // 2. 系统自动同步
    }
}
  1. 内存对齐:
kotlin 复制代码
/**
 * 内存访问优化
 */
class MemoryAccess {
    // 对齐访问更快
    @Aligned(8)
    data class AlignedData(
        val value: Long // 8字节对齐
    )
}
  1. 增量更新:
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/次
            }
        }
    }
}

就像一个高科技仓库:

  1. 智能货架系统(mmap):

    • 直接取放,无需搬运
    • 系统自动同步
  2. 高效分拣(内存缓存):

    • 批量处理订单
    • 减少重复动作
  3. 优化布局(内存对齐):

    • 标准化储位
    • 快速存取货物
  4. 智能补货(增量更新):

    • 按需更新库存
    • 减少整体移动

这就是为什么 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")
    }
}

🎯 预防措施

  1. 代码层面:
markdown 复制代码
- 统一锁的获取顺序
- 实现超时机制
- 添加重试逻辑
- 使用事务机制
  1. 架构层面:
markdown 复制代码
- 减少跨进程操作
- 实现批量写入
- 使用消息队列
- 监控锁的状态

记住:

  1. 统一加锁顺序很重要
  2. 必须有超时机制
  3. 要有重试策略
  4. 监控很关键

如何设计一个支持跨进程事件总线的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()
    )
}

记住:

  1. 适合小数据量的跨进程通信
  2. 需要注意序列化性能
  3. 考虑并发和防抖动
  4. 做好异常处理
相关推荐
踢球的打工仔5 小时前
PHP面向对象(7)
android·开发语言·php
安卓理事人5 小时前
安卓socket
android
程序员小寒11 小时前
前端高频面试题之CSS篇(一)
前端·css·面试·css3
安卓理事人11 小时前
安卓LinkedBlockingQueue消息队列
android
万能的小裴同学12 小时前
Android M3U8视频播放器
android·音视频
进击的野人12 小时前
深入理解 JavaScript Promise:原理、用法与实践
javascript·面试·ecmascript 6
q***577412 小时前
MySql的慢查询(慢日志)
android·mysql·adb
JavaNoober13 小时前
Android 前台服务 "Bad Notification" 崩溃机制分析文档
android
有意义13 小时前
JavaScript 词法作用域与闭包:从底层原理到实战理解
前端·javascript·面试
AY呀13 小时前
黑马喽大闹天宫与JavaScript的寻亲记:作用域与作用域链全解析
前端·javascript·面试