Android性能优化- 从SharedPreferences到MMKV

前言

前面Android性能优化 - 从SharedPreferences跨越到DataStore一文主要介绍了DataStore的实现原理,以及DataStore相对于SharedPreferences的提升,本文主要简述MMKV相对于SharedPreferences存储的使用及优劣势,以及MMKV原理,以及三者对比之下各自的优缺点和使用场景。

浅谈SharedPreferences

SharedPreferences(以下使用SP简称)是在Android中的一种轻量级的存储方式,支持开发者将基本类型数据以键值对的形式进行存储。SP具有在应用内数据可以共享,使用简单方便的优点。

SP会将数据以文件的形式进行存储,具体路径:

/data/data/<packagename>/shared_prefs/

下面对于SP的使用进行简单说明。

  • 首先获取SP对象

使用SP首先需要需要获取SP对象,可以通过Context.getSharedPreferences(key, Context.MODE_PRIVATE)获取到SP对象。

注意⚠️:Mode目前只有Context.MODE_PRIVATE,表示此文件是私有数据,只能够被应用本身访问,写入覆盖。其余的在新版API中均已废弃。

  • 存储数据

var sp = this.getSharedPreferences("test",Context.MODE_PRIVATE)
        var edit = sp.edit()
        edit.putBoolean("flag",true)
        edit.putInt("temp",1)      //统一put完数据再提交,否则可能会空异常
        edit.apply()  //edit.commit()

commit()和apply()都可以提交SP存储,但是两者有所区别:

 1. apply()立刻更改内存中的SP值,但是会异步的将更新写入磁盘,无返回值
 2. commit()则是会同步的写入的磁盘,需要注意其调用的线程(不要放在主线程),有返回值,返回是否成功的写入存储。
  • 取数据

获取到SP对象之后直接get即可:

        var sp = this.getSharedPreferences("test",Context.MODE_PRIVATE)
        var result = sp.getBoolean("flag",false)       //第二个参数是没有获取到数据的默认值

走进MMKV

MMKV是腾讯开源的一款基于mmap内存映射的键值对组件,底层序列化次用了prorobuf实现,性能高且稳定性良好,能够很好的取代SP存储。

1. SP和MMKV优缺点对比

SP虽然简单易用,但是在开发过程中依然存在不少缺点,具体如下:

  1. 多进程共享。系统自带的SP存储对于多进程几乎不支持,并且官方文档上明确指出SP不能使用在多进程上,如果要实现SP存储支持多进程,必须由我们手动去封装ContentProvider实现,实现较复杂并且性能低下。
  2. 数据加密。SP存储实际上是将键值对数据放到本机文件中进行存储,如果需要数据安全需要自己加密。
  3. 效率一般。SP是以xml进行存储的,大量数据不能使用该方式存储。
  4. 只支持基本数据类型,支持存储的数据类型有booleans, floats, ints, longs, and strings。

针对如上的几个缺点,MMKV都进行了改进:

  1. MMKV支持多进行共享。MMKV是基于mmap内存映射的方式,而mmap共享内存本质上是多进程共享的,因此MMKV是支持多进行共享并且效率较高。
  2. 数据加密。MMKV采用了AES CFB-128算法进行加密解密。
  3. MMKV采用了跨平台的protobuf进行序列化和反序列化,比起SP的xml存放方式更加高效。
  4. 支持从SP迁移。MMKV对于SP迁移做了很多支持,项目内如果想由SP迁移到MMKV十分方便。
  5. 支持更多的数据类型,不但支持boolean、int、long、float、double、byte[],还支持String,Set以及实现了Parcelable的类型。

综上所述,MMKV有着速度快,方便易用的优势,下面对其使用进行简单说明。

2. 使用MMKV

1. 包引入及初始化

首先需要引入MMKV包,在buil.gradle中添加如下内容:

implementation 'com.tencent:mmkv:1.2.7'

然后需要在自定义Application中添加初始化:

MMKV.initialize(this)
2. MMKV对象获取

MMKV提供了一个全局的实例,可以直接使用:

var mmkv = MMKV.defaultMMKV()

也可以自定义MMKV对象,设置自定ID

var mmkv2 = MMKV.mmkvWithID("id")

MMKV默认是支持单进程的,如果业务需要多进程访问,需要在初始化的时候添加多进程模式参数:

var mmkv3 = MMKV.mmkvWithID("myId",MMKV.MULTI_PROCESS_MODE)
3. 存取方法

使用MMKV的存取比较简单,方法如下:

//存储方法
mmkv?.encode(key,data)
//获取方法
mmkv?.decodeString(key,"defaultValues")

获取方法需要根据类型进行自己选择。

4. 自定义文件目录

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

val dir = filesDir.absolutePath + "/mmkv_2"
val rootDir = MMKV.initialize(dir)
5. SP迁移

MMKV可以调用importFromSharedPreferences方法进行SP的数据迁移,示例代码如下:
MMKV实现了SharedPreferences,Editor两个接口,所以在迁移之后SP的操作代码可以不用更改。

val mmkv = MMKV.mmkvWithID("myData")
val olderData = DemoApplication.mContext?.getSharedPreferences("myData", MODE_PRIVATE)
mmkv?.importFromSharedPreferences(olderData)
olderData?.edit()?.clear()?.apply()

MMKV优势及实现原理

为了能够理清MMKV的优势,我们首先需要先了解SP的工作原理。

1. SP的工作原理

SP是采用的IO写入数据的,Linux中存在有虚拟内存概念:用户空间和内核空间。
用户空间是用户程序代码运行的地方,内核空间是内核代码运行的地方。为了减小程序崩溃影响,两个控件时隔离的,也就是说即使运行在用户控件的用户程序崩溃,内核也不会受到影响。

SP采用了IO读写的操作,以read为例子说明,具体步骤如下:

  • 从磁盘读取数据,将文件内从硬盘拷贝到内核空间的缓存区。
  • 然后再将数据拷贝到用户控件供程序使用。

从上面两步中我们可以拷贝过程进行了两次,如果数据量比较大,性能损耗也会随之提升。
在这里插入图片描述

2. MMKV实现原理

MMKV的实现原理是mmap(内存映射),它是共享内存的一种(另一种是System V,可用于跨进程),原理如图所示:
在这里插入图片描述
在Linux中,每个进程都有着属于自己的进程控制块(PCB)和地址空间,通过页面将进程的虚拟地址和物理地址进行映射。mmap方法会把文件内容映射到一段虚拟内存上,通过对此段内存的读取和修改,实现对文件的读取和修改。
在mmap之后,并不会将文件内容直接加载到物理页上,只是在虚拟内存中分配了地址空间,当首次访问这段地址时,才会去通过查找页表,但是此时虚拟内存对应的页上没有在物理内存中缓存,则会造成“缺页”,将文件对应内容加载到物理内存上。

相对于普通的IO读写,mmap具有如下优势:

  • 对文件的读取操作跨过了页缓存,减少了数据的拷贝次数,操作内存就相当于操作文件,提高了文件读取效率。
  • 实现了用户空间和内核空间的高效交互方式,修改能够直接反映在映射的区域内,从而被对方空间及时获取。
  • 内存准备:通过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由操作系统负责将内存回写到文件,不必担心 crash 导致数据丢失。
  • 数据组织:数据序列化方面我们选用 protobuf 协议,pb 在性能和空间占用上都有不错的表现。
  • 写入优化:考虑到主要使用场景是频繁地进行写入更新,我们需要有增量更新的能力。我们考虑将增量 kv 对象序列化后,append 到内存末尾。

当然还有如下的缺点:

  • 即使文件很小,甚至于只有几个字节,但是内存的最小粒度是页,因此会占用整页的大小,在连续mmap小文件,会造成内容空间的浪费。
  • 对于变长文件不适合,文件无法完成拓展。
  • 空间增长:使用 append 实现增量更新带来了一个新的问题,就是不断 append 的话,文件大小会增长得不可控。我们需要在性能和空间上做个折中。

3. 了解Protobuf协议

SP存储采用的是xml文件的形式去存储键值对,而MMKV是通过protobuf协议来实现的,存储方式为增量更新,也就是不需要每次修改数据都要重新将所有的数据写入文件,速度上和大小上都优于xml。

protobuf是Google开源的一个序列化框架,类似于xml,json,最大的特点是基于二进制,比一般的xml表示同样的内容要短小的多。想要了解学习的朋友可以在Protobuf官网学习一下,在此不再做更多的说明。

三种方案对比以及各自使用场景

到这里,SP存储,MMKV存储的实现原理已经讲的差不多了,前面一文讲SP存储过渡到DataStore存储所带来的性能优化已经对比过DataStore和SP存储,下面就来一个汇总,对这三种存储做一个综合对比。

功能描述MMKVDataStoreSP
是否阻塞主线程
是否线程安全
是否支持跨进程
是否支持protocol-buffers
是否类型安全
是否能监听到数据变化

我们可以更具具体使用场景选择合适的存储方式:

多进程,跨进程通信,高频同步写入
推荐:MMKV

防止数据丢失,要求搞性能
优先推荐DataStore, 其次sp

防止数据丢失,未使用kotlin协程
推荐sp

总结

相对于SP而言,MMKV和DataStore无论是在速度上还是在文件大小上都更具有优势,是一个很方便易用的框架。但是各自有着各自的优缺点,我们要结合他们各自的优缺点以及使用场景灵活选取,切记没有最好的框架,只有用好的框架。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/202134.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

chrome 调试之 - 给微软小冰看病(无论给小冰发送什么内容都只回复“我已经开始升级啦,期待一下吧!”)

微软 Bing 搜索推出了小冰AI智能聊天模块&#xff0c;具体启用方式是用edge或chrome浏览器打开链接 cn.bing.com 后在输入框搜索任意内容&#xff0c;待搜索结果页面加载完并稍等片刻&#xff0c;页面右侧就会出现一个躲在滚动条后面的小萝莉&#xff0c;抚摸...不&#xff0c;…

elementui中table进行表单验证

<el-form :model"ruleForm" ref"ruleForm" class"demo-ruleForm"><el-table :data"ruleForm.tableDataShou" border style"width: 100%;"><el-table-column type"index" label"序号" wi…

数据预处理:随机裁剪放缩

随机裁剪放缩是一种数据增强技术&#xff0c;可以在训练神经网络时增加数据的多样性&#xff0c;提高模型的泛化能力。具体来说&#xff0c;随机裁剪放缩可以通过随机裁剪和缩放原始图片来生成多个不同的训练样本&#xff0c;从而增加数据集的大小和多样性。这种技术在图像分类…

Flink-时间流与水印

时间流与水印 一、背景二、时间语义1.事件时间&#xff08;event time&#xff09;2.读取时间&#xff08;ingestion time&#xff09;3.处理时间&#xff08;processing time&#xff09; 三、水印-Watermarks1.延迟和正确性2.延迟事件3.顺序流4.无序流5.并行流 四、Windows1.…

Redis对象系统

前言 在Redis中有许多数据结构&#xff0c;比如&#xff1a;简单动态字符串(SDS)&#xff0c;双端链表&#xff0c;字典&#xff0c;压缩列表&#xff0c;整数集合等。 Redis并没有直接使用这些数据结构来实现键值对数据库&#xff0c;而是基于这些数据结构创建了一个对象系统。…

脚本格式问题记录

服务器上的一些脚本迁移到其他服务上发生的小问题 问题&#xff1a;执行一个在win10系统编写好的shell脚本&#xff0c;放到Linux上执行报错如下&#xff1a; bash: ./xxx.sh: /bin/bash^M: bad interpreter: No such file or directory 原因&#xff1a;window系统写的脚本&a…

【Spring Boot 源码学习】BootstrapRegistryInitializer 详解

Spring Boot 源码学习系列 BootstrapRegistryInitializer 详解 引言往期内容主要内容1. 初识 BootstrapRegistryInitializer2. 加载 BootstrapRegistryInitializer3. BootstrapRegistryInitializer 的初始化 总结 引言 书接前文《初识 SpringApplication》&#xff0c;我们从 …

A*算法学习

系列文章目录 前言 在总结 2023华为软件精英挑战赛——全赛段思路分享与总结 - 知乎 (zhihu.com)时&#xff0c;发现自己还有很多技术细节没搞懂&#xff0c;这里看静态全局路径规划最常见的A*算法&#xff0c;这个博主讲得很好&#xff1a; A-Star&#xff08;A*&#xff0…

第十五届蓝桥杯(Web 应用开发)模拟赛 2 期-大学组(详细分析解答)

目录 1.相不相等 1.1 题目要求 1.2 题目分析 1.3 源代码 2.三行情书 2.1 题目要求 2.2 题目分析 2.3 源代码 3.电影院在线订票 3.1 题目要求 3.2 题目分析 3.3 源代码 4.老虎坤&#xff08;不然违规发不出来&#xff09; 4.1 题目要求 4.2 题目分析 4.3 源代码 …

mac 聚焦搜索不显示

我是连搜索框都不显示&#xff0c;不是搜索结果显示异常 点右上角的搜索按钮都毫无反应 我检查过快捷键之类的设置&#xff0c;都正常&#xff0c;最后是通过删除文件解决的 cd ~/Library/Preferences/ rm com.apple.Spotlight.plist 重启 mac 参考 Spotlight Search Not W…

“rhdf5filters.so’ not found when install ‘glmGamPoi‘ package

在R中安装glmGamPoi包的时候&#xff0c;出现了如下报错&#xff1a; install.packages(glmGamPoi) 尝试方案一&#xff1a; sudo apt install pkg-config libhdf5-dev安装lighdf5-dev&#xff0c;并将安装路径链接至usr/lib/文件。 locate rhdf5filters.so sudo ln -s /hom…

java-var类型推断的使用时机

写在前面&#xff1a; 在jdk9的时候引入了var关键字&#xff0c;但是这是一把双刃剑&#xff0c;使用的好的话可以简化代码提高可读性&#xff0c;如果使用的不好的话会导致反效果。 文章目录 使用原则推荐使用时机new关键字创建对象类型不重要for循环 不适合与泛型大量结合字…

【Java学习笔记】75 - 算法优化入门 - 马踏棋盘问题

一、意义 1.算法是程序的灵魂&#xff0c;为什么有些程序可以在海量数据计算时&#xff0c;依然保持高速计算? 2.拿老韩实际工作经历来说&#xff0c;在Unix下开发服务器程序&#xff0c;功能是要支持上千万人同时在线&#xff0c;在上线前&#xff0c; 做内测&#xff0c;一…

vuepress-----9、PWA

# 9、PWA 使用babel 的插件形式 [vuepress/pwa,{serviceWorker: true,updatePopup: {message: "New content is available.",buttonText: "Refresh"}}]提供 Manifest 和 icons (opens new window) 拷贝到public目录下 发布后出现 service workers [外链图片…

Spring第三课,Lombok工具包下载,对应图书管理系统列表和登录界面的后端代码,分层思想

目录 一、Lombok工具包下载 二、前后端互联的图书管理系统 规范 三、分层思想 三层架构&#xff1a; 1.表现层 2.业务逻辑层 3.数据层 一、Lombok工具包下载 这个工具包是为了做什么呢&#xff1f; 他是为了不去反复的设置setting and getting 而去产生的工具包 ⚠️工具…

二叉树(判断是否为对称二叉树)

题目&#xff08;力扣&#xff09;&#xff1a; 观察题目&#xff0c;只需判断该二叉树是否对称。 判断二叉树是否对称&#xff0c;就可以换位去判断该二叉树的左子树和右子树是否对称。 这时就可以写一个辅助函数来方便判断。 该函数是判断两颗树是否镜像对称&#xff0c;这…

【华为数通HCIP | 网络工程师】821刷题日记-IS-IS(2)

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

Docker—更新应用程序

在本部分中&#xff0c;你将更新应用程序和映像。您还将了解如何停止和移除容器。 一、更新源代码 在以下步骤中&#xff0c;当您没有任何待办事项列表项时&#xff0c;您将把“空文本”更改为“您还没有待办事项&#xff01;在上面添加一个&#xff01;” 1、在src/static/…

电子学会C/C++编程等级考试2022年12月(三级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:鸡兔同笼 一个笼子里面关了鸡和兔子(鸡有2只脚,兔子有4只脚,没有例外)。已经知道了笼子里面脚的总数a,问笼子里面至少有多少只动物,至多有多少只动物。 时间限制:1000 内存限制:65536输入 一行,一个正整数a (a < 327…

分发测试应用平台怎么用之应用详情功能

我的应用 应用功能引导 ●您会看到以下页面&#xff0c;下图为功能的解释方便您的运行 我的应用-详情-应用详情 ●我们点击应用详情数字③&#xff0c;点击应用详情&#xff0c;下图是对详情页的功能介绍。 详情-应用设置 ●详情-应用设置-下图为应用设置的上半部分 ●下图为应…