Android存储:轻松掌握MMKV

写在前面

前面我写了一篇文章讲解了 DataStore 的使用:Jetpack学习:轻松掌握DataStore - 掘金 (juejin.cn) 后面了解到了 MMKV ,让我们先看看它的介绍:

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

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

参考文章

官方文档:
MMKV/README_CN.md at master · Tencent/MMKV (github.com)

但官方文档讲得不够全面,所以还参考了其他优秀文章:
Android:MMKV 组件入门 - 简书 (jianshu.com)
MMKV------Android上的使用(替换SP存储) - 掘金 (juejin.cn)

准备

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

xml布局如下:

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>  
<androidx.constraintlayout.widget.ConstraintLayout     
    xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:app="http://schemas.android.com/apk/res-auto"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    tools:context=".MainActivity">  
  
    <TextView        
	    android:id="@+id/tv"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="0"  
        android:textSize="28sp"  
        app:layout_constraintBottom_toBottomOf="parent"  
        app:layout_constraintEnd_toEndOf="parent"  
        app:layout_constraintStart_toStartOf="parent"  
        app:layout_constraintTop_toTopOf="parent" />  
        
	<com.google.android.material.floatingactionbutton.FloatingActionButton  
	    android:id="@+id/fab_delete"  
	    android:layout_width="wrap_content"  
	    android:layout_height="wrap_content"  
	    android:layout_marginBottom="12sp"  
	    android:layout_marginEnd="24dp"  
	    android:contentDescription="delete"  
	    android:src="@drawable/ic_baseline_delete_forever_24"  
	    app:background="@android:color/holo_red_dark"  
	    app:backgroundTint="@android:color/holo_red_dark"  
	    app:layout_constraintBottom_toTopOf="@+id/fab_add"  
	    app:layout_constraintEnd_toEndOf="parent"  
	    app:tint="@color/white" />
  
    <com.google.android.material.floatingactionbutton.FloatingActionButton        
	    android:id="@+id/fab_add"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_margin="24dp"  
        android:src="@drawable/ic_baseline_exposure_plus_1_24"  
        app:backgroundTint="@color/black"  
        app:background="@color/black"  
        app:tint="@color/white"  
        app:layout_constraintBottom_toBottomOf="parent"  
        app:layout_constraintEnd_toEndOf="parent"  
        android:contentDescription="add" />  
  
</androidx.constraintlayout.widget.ConstraintLayout>

activity代码如下:

kotlin 复制代码
class MainActivity : AppCompatActivity() {  
	
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_main)  
		  
        val tv = findViewById<TextView>(R.id.tv)  
        val add = findViewById<FloatingActionButton>(R.id.fab_add)
        val delete = findViewById<FloatingActionButton>(R.id.fab_delete)  
    }  
}

我们的最终目的是通过不断点击 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)

gradle 复制代码
dependencies {
    // 将1.3.1替换为最新的版本
    implementation 'com.tencent:mmkv:1.3.1'
}
初始化

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

kotlin 复制代码
class GlobalApplication : Application() {  
    override fun onCreate() {  
        super.onCreate()  
        // 初始化  
        MMKV.initialize(this)  
    }  
}

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

kotlin 复制代码
String dir = getFilesDir().getAbsolutePath() + "/mmkv"; 
String rootDir = MMKV.initialize(dir);

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

kotlin 复制代码
// 获取 MMKV 默认全局实例,一般选用这个,本文也是选择这个进行实例创建
val mmkv = MMKV.defaultMMKV()

// 根据设置 id 来自定义 MMKV 对象。比如根据业务来区分的存取实例
val mmkv = MMKV.mmkvWithID("ID")  

// 开启多进程访问。默认是单线程
val mmkv = MMKV.mmkvWithID("ID",MMKV.MULTI_PROCESS_MODE)

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

kotlin 复制代码
val key = "number"
支持类型

支持以下 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

kotlin 复制代码
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);  
}

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

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

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

MMKV 提供了便利的 API 可以直接写入数据: public boolean encode(String key, T value)

其中 T 为 MMKV 可支持的类型,在上文已经提过了。 现在我们就可以直接实现点击 fab_add 按钮来修改数据

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

删除单个key:removeValueForKey(String key)

删除多个key:removeValuesForKeys(String[] arrKeys)

清空所有数据:clearAll()

在这里因为我们只有一个 key ,所以直接使用removeValueForKey来清除我们的数据,同时更新 TextView 的 UI

kotlin 复制代码
delete.setOnClickListener {
    // 清除数据
    mmkv.removeValueForKey(key)  
    // 更新 TextView 数据
    tv.text = mmkv.decodeInt(key, 0).toString()  
}
从 SharedPreferences 迁移到 MMKV

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

kotlin 复制代码
val sp = getSharedPreferences("test", Context.MODE_PRIVATE)  
val edit = sp.edit()  
// 为了验证顺利迁移,我们初始值设置为10  
edit.putInt("number",10);  
edit.apply()

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

好的现在我们来进行迁移,MMKV 提供了 importFromSharedPreferences 方法让我们方便进行 SP 数据的迁移,只要提供需要迁移的 SP 实例就能非常快速完成实例。

MMKV 实现了 SharedPreferences,Editor两个接口,所以在迁移之后SP的操作代码可以不用更改。

kotlin 复制代码
val oldData = getSharedPreferences("test",Context.MODE_PRIVATE)  
mmkv.importFromSharedPreferences(oldData)  
oldData.edit().clear().apply()

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

完整代码

Application:

kotlin 复制代码
class GlobalApplication : Application() {  
    override fun onCreate() {  
        super.onCreate()  
        // 初始化  
        MMKV.initialize(this)  
    }  
}

Activity:

kotlin 复制代码
class MainActivity : AppCompatActivity() {  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_mmkv)  
		  
        val tv = findViewById<TextView>(R.id.tv_mmkv)  
        val add = findViewById<FloatingActionButton>(R.id.fab_add)  
        val delete = findViewById<FloatingActionButton>(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()  
        }  
    }  
}
相关推荐
松仔log4 小时前
Java中级——组合和继承
android·java·开发语言
我命由我123458 小时前
Jetpack Compose - MaterialExpressiveTheme 与 MaterialTheme、ColorScheme
android·java·开发语言·java-ee·kotlin·android jetpack·android runtime
lilian2339 小时前
Harmony os 技术实战|拼豆制图06:收藏 ID、生成记录与重启恢复怎么不打架
android·java·数据库·harmonyos
2601_955759629 小时前
ClaudeAPI成本中心与业务标签设计指南
android·java·数据库
余衫马9 小时前
2026最新版!Android Studio 极速安装与配置指南
android·ide·android studio
AFinalStone9 小时前
Android 7系统异常问题排查(五)Framework层(下)—System Server崩溃
android·tombstone·系统异常
AFinalStone9 小时前
Android 7系统异常问题排查(八)系统追踪—Trace机制与性能诊断
android·系统异常
极客猴子13 小时前
Android录音转写频繁卡顿?多款APP长时间会议场景稳定性实测
android·人工智能·智能手机·飞书
Kapaseker13 小时前
Boolean 变量到底该怎么命名?
android·kotlin
AFinalStone13 小时前
Android 7系统异常问题排查(六)应用异常(上)—ANR机制全解
android·系统异常