• Android存储:轻松掌握MMKV


    MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。从 2015 年中至今在微信上使用,其性能和稳定性经过了时间的验证。近期也已移植到 Android / macOS / Win32 / POSIX 平台,一并开源。

    可以看出它也可以用于替代 SP,操作也与 SP 类似,相较于 DataStore 更好上手,所以我干脆也写一篇文章介绍下如何使用 MMKV。

    准备

    我们通过一个计数器例子,在具体的情景中理解和使用 MMKV
    xml布局如下:

      
      
      
          
            
    	
      
          
      
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    activity代码如下:

    class MainActivity : AppCompatActivity() {  
    	
        override fun onCreate(savedInstanceState: Bundle?) {  
            super.onCreate(savedInstanceState)  
            setContentView(R.layout.activity_main)  
    		  
            val tv = findViewById(R.id.tv)  
            val add = findViewById(R.id.fab_add)
            val delete = findViewById(R.id.fab_delete)  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    我们的最终目的是通过不断点击 fab_add 使得 TextView 内的数字不断+1,然后点击 fab_delete 将 TextView 内的数字清零。

    开始使用
    添加依赖

    Github:Tencent/MMKV: An efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, and POSIX. (github.com)

    dependencies {
        // 将1.3.1替换为最新的版本
        implementation 'com.tencent:mmkv:1.3.1'
    }
    
    • 1
    • 2
    • 3
    • 4
    初始化

    实现应用的存储功能通常在App启动时就要开始初始化其对象,所以我们在自定义 Application 内对 MMKV 初始化。

    class GlobalApplication : Application() {  
        override fun onCreate() {  
            super.onCreate()  
            // 初始化  
            MMKV.initialize(this)  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    MMKV 默认把文件存放在$(FilesDir)/mmkv/目录。可以在 MMKV初始化时自定义根目录

    String dir = getFilesDir().getAbsolutePath() + "/mmkv"; 
    String rootDir = MMKV.initialize(dir);
    
    • 1
    • 2

    MMKV 为我们提供一个全局的实例,可以直接使用

    // 获取 MMKV 默认全局实例,一般选用这个,本文也是选择这个进行实例创建
    val mmkv = MMKV.defaultMMKV()
    
    // 根据设置 id 来自定义 MMKV 对象。比如根据业务来区分的存取实例
    val mmkv = MMKV.mmkvWithID("ID")  
    
    // 开启多进程访问。默认是单线程
    val mmkv = MMKV.mmkvWithID("ID",MMKV.MULTI_PROCESS_MODE)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    与 SP 类似,我们需要定义一个 key 值用于读取写入 MMKV 中的数据

    val key = "number"
    
    • 1
    支持类型

    支持以下 Java 语言基础类型:

    • boolean、int、long、float、double、byte[]
    • 支持以下 Java 类和容器:
    • String、Set< String >
    • 任何实现了 Parcelable 的类型
    读取数据

    MMKV 为用户读取数据提供了两种类型的函数,其中T为上述的 MMVK 支持类型

    • decodeT(String key):读取指定 key 中的值
    • decodeT(String key, T defaultValue):读取指定 key 中的值,如果该 key 不存在则赋予默认值并返回。

    其中需要注意的是,第一个函数虽然没有明面上赋予默认值,但查看源码,可以发现如果 key 不存在时 MMKV 也会为其设置固定默认值并返回,以 decodeInt 为例子,MMKV 会设置初始默认值 0

    public int decodeInt(String key) {  
        return this.decodeInt(this.nativeHandle, key, 0);  
    }
    public int decodeInt(String key, int defaultValue) {  
        return this.decodeInt(this.nativeHandle, key, defaultValue);  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    以下为其他类型 MMKV 赋予的初始默认值

    在计数器案例中,我们选择 int 类型数据

    // 初始化 TextView 的值  
    // 获取存储在本地的 number 值,如果不存在 mmkv 返回默认值为 0
    tv.text = mmkv.decodeInt(key).toString()
    
    • 1
    • 2
    • 3
    写入数据

    MMKV 提供了便利的 API 可以直接写入数据: public boolean encode(String key, T value)
    其中 T 为 MMKV 可支持的类型,在上文已经提过了。 现在我们就可以直接实现点击 fab_add 按钮来修改数据

    add.setOnClickListener {  
        // 由于点击 FloatingActionButton 值会增加,所以 value 值在原先存储值的基础上 +1    
        val value = mmkv.decodeInt(key) + 1  
        // 将数据写入 MMVK    
        mmkv.encode(key, value)  
        // 更新 TextView 的值 
        tv.text = value.toString()  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    删除数据

    删除单个key:removeValueForKey(String key)
    删除多个key:removeValuesForKeys(String[] arrKeys)
    清空所有数据:clearAll()
    在这里因为我们只有一个 key ,所以直接使用removeValueForKey来清除我们的数据,同时更新 TextView 的 UI

    delete.setOnClickListener {
        // 清除数据
        mmkv.removeValueForKey(key)  
        // 更新 TextView 数据
        tv.text = mmkv.decodeInt(key, 0).toString()  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    从 SharedPreferences 迁移到 MMKV

    为了演示怎么迁移,我们重头再来,在准备部分的代码基础上临时创建 SharedPreferences 储存数据。

    val sp = getSharedPreferences("test", Context.MODE_PRIVATE)  
    val edit = sp.edit()  
    // 为了验证顺利迁移,我们初始值设置为10  
    edit.putInt("number",10);  
    edit.apply()
    
    • 1
    • 2
    • 3
    • 4
    • 5

    运行程序后查看数据已经成功保存在本地

    好的现在我们来进行迁移,MMKV 提供了 importFromSharedPreferences 方法让我们方便进行 SP 数据的迁移,只要提供需要迁移的 SP 实例就能非常快速完成实例。
    MMKV 实现了 SharedPreferences,Editor两个接口,所以在迁移之后SP的操作代码可以不用更改。

    val oldData = getSharedPreferences("test",Context.MODE_PRIVATE)  
    mmkv.importFromSharedPreferences(oldData)  
    oldData.edit().clear().apply()
    
    • 1
    • 2
    • 3

    其他代码如同前面,现在重新运行,可以看到界面初始数值为10,说明迁移数据成功

    完整代码

    Application:

    class GlobalApplication : Application() {  
        override fun onCreate() {  
            super.onCreate()  
            // 初始化  
            MMKV.initialize(this)  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Activity:

    class MainActivity : AppCompatActivity() {  
      
        override fun onCreate(savedInstanceState: Bundle?) {  
            super.onCreate(savedInstanceState)  
            setContentView(R.layout.activity_mmkv)  
    		  
            val tv = findViewById(R.id.tv_mmkv)  
            val add = findViewById(R.id.fab_add)  
            val delete = findViewById(R.id.fab_delete)  
              
            // 获取 MMKV 实例  
            val mmkv = MMKV.defaultMMKV()  
            // 设置 key 值  
            val key = "number"  
            
            // 数据迁移
            val oldData = getSharedPreferences("test",Context.MODE_PRIVATE)  
            mmkv.importFromSharedPreferences(oldData)  
            oldData.edit().clear().apply()
    		  
            // 初始化 TextView 的值  
            // 获取存储在本地的 number 值,如果不存在则默认值为 0        
            tv.text = mmkv.decodeInt(key, 0).toString()  
    		  
            add.setOnClickListener {  
                // 由于点击 FloatingActionButton 值会增加,所以 value 值 +1            
                val value = mmkv.decodeInt(key) + 1  
                // 将数据写入 MMVK            
                mmkv.encode(key, value)  
                // 更新 TextView            
                tv.text = value.toString()  
            }  
    		  
            delete.setOnClickListener {  
                // 清除数据  
                mmkv.removeValueForKey(key)  
                // 更新 TextView 数据  
                tv.text = mmkv.decodeInt(key, 0).toString()  
            }  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    Android 学习笔录

    Android 性能优化篇:https://qr18.cn/FVlo89
    Android Framework底层原理篇:https://qr18.cn/AQpN4J
    Android 车载篇:https://qr18.cn/F05ZCM
    Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
    Android 音视频篇:https://qr18.cn/Ei3VPD
    Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
    OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
    Kotlin 篇:https://qr18.cn/CdjtAF
    Gradle 篇:https://qr18.cn/DzrmMB
    Flutter 篇:https://qr18.cn/DIvKma
    Android 八大知识体:https://qr18.cn/CyxarU
    Android 核心笔记:https://qr21.cn/CaZQLo
    Android 往年面试题锦:https://qr18.cn/CKV8OZ
    2023年最新Android 面试题集:https://qr18.cn/CgxrRy
    Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
    音视频面试题锦:https://qr18.cn/AcV6Ap

  • 相关阅读:
    javaSE- 方法的使用
    【NOI模拟赛】摆(线性代数,杜教筛)
    网址导航收藏引导页面H5源码(自适应引导页HTML源码)-自动检测域名延迟
    Spring MVC执行流程
    技术学习:Python(08)|操作MySQL
    Python 全栈系列209 so_pack
    Python机器视觉--OpenCV进阶(核心)--图像二值化
    学习java第二天
    我的NVIDIA开发者之旅——优化显卡性能
    Spring通知类型及使用ProxyFactoryBean创建AOP代理
  • 原文地址:https://blog.csdn.net/maniuT/article/details/134057844