Android 数据存储(二)-SP VS DataStore VS MMKV

2023-05-16

一、SharedPreferences

        不同于文件的存储方式,如果要保存的键值集合相对较小,则应使用SharedReferences API。SharedReferences对象指向一个包含键值对的文件,并提供简单的读写方法。

        本文从SharedReferences开始逐步引入Preference、MMKV。

1.1 获取SharedPreferences对象

        要想使用SharedPreferences来存储数据,首先需要获取到SharedPreferences 对象。Android中主要提供了2种方法用于得到SharedPreferences 对象。

Context 类中的getSharedPreferences()方法

此方法接收两个参数:

  • 第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存放在/data/data/<package name>/shared_prefs/目录下的。

  • 第二个参数用于指定操作模式,目前只有MODE_PRIVATE这一种模式可选,它是默认的操作模式,和直接传入0效果是相同的,表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写。

Activity 类中的getPreferences()方法

        这个方法和Context中的getSharedPreferences() 方法很相似,不过它只接收一个操作模式参数,因为使用这个方法时会自动将当前活动的类名作为SharedPreferences的文件名。

注意:自API级别17以来,已弃用MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE模式。如果使用则抛出SecurityException。

        这里使用的是Context 类中的getSharedPreferences()方法

public class SPUtils {
    //保存在手机里面的文件名
    public static final String FILE_NAME = "scc_data";

    private static final SPUtils spUtils = new SPUtils();
    public static SPUtils getInstance() {
        return spUtils;
    }
    private SPUtils() {
    }
    private SharedPreferences getSP() {
        return AppGlobalUtils.getApplication().getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
    }
}

文件保存路径:/data/data/com.scc.datastorage/shared_prefs/scc_data.xml

1.2 写入数据

        通过putInt()和putString()等方法传递要写入的键和值。然后调用apply()或commit()保存更改。

    private void putSp() {
        SPUtils.getInstance().put("name","Scc");
        SPUtils.getInstance().put("age",20);
    }
    
    /**
     * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
     * 如果仅用到String等个别类型,可单独写方法
     */
    public void put(String key, Object value) {
        if(MStringUtils.isNullOrEmpty(key)){
            return;
        }
        SharedPreferences.Editor editor = getSP().edit();
        if (value instanceof String) {
            editor.putString(key, (String)value);
        } else if (value instanceof Integer) {
            editor.putInt(key, (Integer) value);
        } else if (value instanceof Boolean) {
            editor.putBoolean(key, (Boolean) value);
        } else if (value instanceof Float) {
            editor.putFloat(key, (Float) value);
        } else if (value instanceof Long) {
            editor.putLong(key, (Long) value);
        }
        SharedPreferencesCompat.apply(editor);
    }

1.3 读取数据

        调用getInt()和getString()等方法从SharedPreference获取数据,提供所需值的键,如果键不存在,还可以选择返回默认值。

    private void getSp() {
        Log.e("SP","SpString:"+SPUtils.getInstance().getString("name"));
        Log.e("SP","SpInt:"+SPUtils.getInstance().getInt("age"));
    }
    
    //获取String类型数据
    public String getString(String key) {
        if (MStringUtils.isNullOrEmpty(key)) {
            return "";
        }
        return getSP().getString(key, "");
    }
    //获取Int类型数据
    public Integer getInt(String key) {
        if (MStringUtils.isNullOrEmpty(key)) {
            return 0;
        }
        return getSP().getInt(key, 0);
    }

1.4 删除数据

        调用 remove() 方法从SharedPreference删除数据,提供所需值的键。

    private void removeSp() {
        SPUtils.getInstance().remove("name");
        Log.e("SP","Remove:"+SPUtils.getInstance().getString("name"));
        Log.e("SP","SpInt:"+SPUtils.getInstance().getInt("age"));
    }
    //移除某个key值已经对应的值
    public void remove(String key) {
        if (!MStringUtils.isNullOrEmpty(key)) {
            SharedPreferences.Editor editor = getSP().edit();
            editor.remove(key);
            SharedPreferencesCompat.apply(editor);
        }
    }

        然后咱们再看看scc_data.xml的内容:

        一些简单的键值对存储可以使用SharedPreference,如登录的账号密码和一些基本的用户信息。

二、Jetpack Preferences

        DataStore是 Android Jetpack 中的一个组件,它是一个数据存储的解决方案,跟SharedPreferences一样,采用key-value形式存储。Jetpack DataStore 是经过改进的新版数据存储解决方案,旨在取代 SharedPreferences,让应用能够以异步、事务方式存储数据。

Jetpack DataStore 你总要了解一下吧?_帅次的博客-CSDN博客

三、MMKV—基于 mmap 的高性能通用 key-value 组件

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

git 地址

3.1 MMKV 优势

  • 非常高效:MMKV使用mmap与文件保持内存同步,,由操作系统负责将内存回写到文件,不必担心 crash 导致数据丢失。使用protobuf对数值进行编码/解码,充分利用Android,实现最佳性能。

  • 多进程并发:MMKV支持进程之间的并发读写访问。

  • 易于使用的:你可以随时使用MMKV。所有的更改都会立即保存,不需要同步,也不需要apply调用。跟SharedReferences用法类似,可直接

  • 少数几个文件(小):MMKV包含进程锁、编码/解码帮助程序和mmap逻辑等等。很整洁。

3.2 性能对比(摘自MMKV官方)

        我们将 MMKV 和 SharedPreferences、SQLite 进行对比,重复学习操作 1k 次。相关的测试代码在。结果变成了Android/MMKV/mmkvdemo/图表。

        (测试操作机器是华为Mate 20 Pro 128G,Android 10,每组重复1k次,时间单位是ms)

        单性能进程可见:MMKV上超越共享的 Preferences & SQLite,在读取性能上也有相近或超越的表现。

        多进程的性能:MMMKV 无论是在写入性能还是在读取性能,都远远超越 MultiProcessSharedPreferences & SQLite & SQLite,MMKV 在 Android 多进程 key-value 存储组件上是不二之选。

3.3 Android 接入指南

        3.3.1 引入依赖

        在你项目的app_module对应的build.gradle中添加如下依赖:

dependencies {
    implementation 'com.tencent:mmkv:1.2.12'
}

        3.3.2 初始化

        MMKV 的使用非常简单,所有变更立马生效,无需调用 sync、apply。 在 App 启动时初始化 MMKV,设定 MMKV 的根目录(files/mmkv/),最好在在 Application 里:

    public void onCreate() {
        super.onCreate();
        //初始化MMKV
        String rootDir = MMKV.initialize(this);
        Log.e("SP","mmkv root: " + rootDir);
    }

        3.3.3 写入数据

    private void putMMKV() {
        MMKVUtils.getInstance().encode("name","mmkv-Scc");
        MMKVUtils.getInstance().encode("age",40);
    }
    //保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
    public void encode(String key, Object object) {
        if (object instanceof String) {
            mmkv.encode(key, (String) object);
        } else if (object instanceof Integer) {
            mmkv.encode(key, (Integer) object);
        } else if (object instanceof Boolean) {
            mmkv.encode(key, (Boolean) object);
        } else if (object instanceof Float) {
            mmkv.encode(key, (Float) object);
        } else if (object instanceof Long) {
            mmkv.encode(key, (Long) object);
        } else if (object instanceof Double) {
            mmkv.encode(key, (Double) object);
        } else if (object instanceof byte[]) {
            mmkv.encode(key, (byte[]) object);
        } else {
            mmkv.encode(key, object.toString());
        }
    }
    //保存任何Parcelable的类型
    public void encode(String key, Parcelable parcelable) {
        Log.e("mmkv","Parcelable.start"+parcelable.getClass());
        boolean isTrue = mmkv.encode(key, parcelable);
        Log.e("mmkv","Parcelable.end"+isTrue);
    }

        你会发现他跟上面的SharedReferences基本一致。当然MMKV存储支持的数据类型远远多于SharedReferences。 支持的数据类型

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

    • boolean、int、long、float、double、byte[]

  • 支持以下 Java 类和容器:

    • String、Set<String>

    • 任何 Parcelable 的类型

        3.3.4 读取数据

    private void getMMKV() {
        Log.e("SP","MMKVString:"+MMKVUtils.getInstance().decodeString("name"));
        Log.e("SP","MMKVInt:"+MMKVUtils.getInstance().decodeInt("age"));
    }
    public String decodeString(String key) {
        return mmkv.decodeString(key, "");
    }
    public Integer decodeInt(String key) {
        return mmkv.decodeInt(key, 0);
    }    

        3.3.5 移除数据

    private void removeMMKV() {
        MMKVUtils.getInstance().removeValueForKey("name");
        Log.e("SP","Remove:"+MMKVUtils.getInstance().decodeString("name"));
        Log.e("SP","MMKVInt:"+MMKVUtils.getInstance().decodeInt("age"));
    }
    //移除某个key对
    public void removeValueForKey(String key) {
        mmkv.removeValueForKey(key);
    }

    //同时移除多个key对
    public void removeValuesForKeys(String[] strings) {
        mmkv.removeValuesForKeys(strings);
    }

    //清除所有key
    public void clearAll() {
        mmkv.clearAll();
    }

        3.3.6 添加数据扩展

  • 不同业务需要区别存储,可以单独创建自己的实例。

  • 业务需要多进程访问,那么在初始化的时候加上标志位 MMKV.MULTI_PROCESS_MODE

3.4 华为手机慎用腾讯MMKV框架

问题描述

        使用MMKV替换原生SharedPreference,在某些华为手机上,配置文件会莫名其妙丢失,整个应用像重新安装一样。

华为手机慎用腾讯MMKV框架

        那么你可以自定义文件存储路径

        默认路径:/data/data/com.scc.datastorage(你的包名)/files/mmkv

    //自定义文件路径。
    String dir = getFilesDir().getAbsolutePath()+"mmkv-z2";
    String dirPath = MMKV.initialize(this,dir);
    
    //mmkv源码  
    public static String initialize(Context context, String rootDir) {
        MMKVLogLevel logLevel = MMKVLogLevel.LevelInfo;
        return initialize(context, rootDir, (MMKV.LibLoader)null, logLevel);
    }

3.5 SharedPreferences 迁移

  • MMKV 提供了 importFromSharedPreferences() 函数,可以比较方便地迁移数据过来。

  • MMKV 还额外实现了一遍 SharedPreferences、SharedPreferences.Editor 这两个 interface,在迁移的时候只需两三行代码即可,其他 CRUD 操作代码都不用改。

    private void importSharedPreferences() {
        //SharedPreferences preferences = getSharedPreferences("myData", MODE_PRIVATE);
        MMKV preferences = MMKV.mmkvWithID("myData");
        // 迁移旧数据
        {
            SharedPreferences old_man = getSharedPreferences("myData", MODE_PRIVATE);
            preferences.importFromSharedPreferences(old_man);
            old_man.edit().clear().commit();
        }
        // 跟以前用法一样
        SharedPreferences.Editor editor = preferences.edit();
        editor.putBoolean("bool", true);
        editor.putInt("int", Integer.MIN_VALUE);
        editor.putLong("long", Long.MAX_VALUE);
        editor.putFloat("float", -3.14f);
        editor.putString("string", "hello, imported");
        HashSet<String> set = new HashSet<String>();
        set.add("W"); set.add("e"); set.add("C"); set.add("h"); set.add("a"); set.add("t");
        editor.putStringSet("string-set", set);
        // 无需调用 commit()
        //editor.commit();
    }

3.6 macOS / Win32 / POSIX 平台

        其他平台可前往官方文档自行接入。

四、相关链接

Android 数据全方案处理

MMKV-git 地址

Jetpack Preferences

Android 数据存储(一)-文件存储

Android 数据存储(三)-SQLite数据库实例

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 数据存储(二)-SP VS DataStore VS MMKV 的相关文章

  • 如何创建在 React-Native 中检测自动位置的地图

    我已经在react native中创建了地图 参考https github com lelandrichardson react native maps https github com lelandrichardson react nat
  • Android TelecomManager 中的 addIncomingCall 没有执行任何操作

    我正在尝试使用本机 Android 来电 UI 我有一个连接服务 并且我已经成功注册了一个电话帐户 但在我调用方法 addNewIncomingCall 后什么也没有发生 对我所缺少的有什么想法吗 显现
  • 如何制作像 Google+ 应用程序中那样的气泡? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 有谁知道如何使用 Google An
  • Android中将JSON数据存储到本地数据库

    好的 我创建了一个应用程序 它使用 JSON 从我的服务器检索数据 现在我想将检索到的数据存储在手机的本地存储 数据库中 我该怎么做 我是android编程新手 这是我从服务器收到的 JSON messages id 44 issender
  • Cordova - 启动后出现白屏,控制台中没有例外

    我已经离开我的 Cordova 应用程序一段时间了 但昨天刚刚进行了一次新的克隆 发现它出现了 死机白屏 症状 启动画面显示 程序加载 然后我就得到一个空白屏幕 更多细节 CLI 科尔多瓦 6 1 1 安卓 5 1 1 ios 4 1 1
  • 虚拟回调接口

    在 Eclipse 为您创建的来自 Google 的示例主从流代码中 片段中包含以下内容 private Callbacks mCallbacks sDummyCallbacks public interface Callbacks pub
  • 我无法再在后台应用程序中接收任何 FCM 消息

    当应用程序处于后台时 我无法再在应用程序中接收任何数据消息 请注意 直到最近它在我的应用程序中都运行良好 也许在我的开发环境最近更新后它停止工作了 我不能说 所以我尝试用快速入门 android 项目 https github com fi
  • 如何在不使用 Firebase 控制台的情况下发送 Firebase 云消息通知?

    我从新的 Google 通知服务开始 Firebase Cloud Messaging 感谢这段代码https github com firebase quickstart android tree master messaging htt
  • 当我单击“完成”键时,Android OnEditorActionListener() actionId 给出 0

    我创建了一个键盘 当用户输入数字时 它会输入特定的 EditText 但是当用户单击Done关键 它没有去setOnEditorActionListener但它关闭了键盘 这是我的代码 final EditText txtQty new E
  • 4 天后,应用仍未在 Google Play 搜索中编入索引

    我已经在 4 天前在 google play 上发布了我的第一个应用程序 语音到短信 但我仍然无法通过其名称或开发人员名称在搜索中找到我的应用程序 我只能通过包名称或真实应用程序名称 但不带空格 找到我的应用程序 VoiceToSMS 链接
  • 如何使用 Google 的 GithubBrowserSample 方法在片段之间共享视图模型?

    我对 Android 架构组件的使用非常陌生 因此我决定使用 GithubBrowserSample 来构建我的应用程序来实现我的许多用例 但我有一个问题 我不知道使用这种方法在片段之间共享视图模型的正确方法是什么 我想共享视图模型 因为我
  • 使用 DataBinding 测试片段时膨胀类 Fragment 时出错

    有人可以帮我解决这个问题吗 在我的测试中 我想检查登录按钮是否显示在起始页上 今天我已经为这个错误苦苦挣扎了一段时间 我不知道应该如何解决它 我在这里错过了什么 也许这个问题与这个问题有些相似 Espresso 测试失败 并显示 java
  • 如何在同一设备上运行的 Android 应用程序之间传输文件?

    我正在编写一个与 RESTful 服务交互的 Android 应用程序 该 Web 服务本质上是一个文件系统 并提供元数据以及对文件的 CRUD 访问 我的应用程序检索元数据 并通过ContentProvider 我需要添加与我的应用程序在
  • 我在 android studio 中使用 kotlin 时出现错误

    为什么会出现这个错误 09 12 16 36 31 502 1886 1886 com getloction nourmedhat smartgate getlocation E AndroidRuntime 致命异常 main 进程 co
  • Android - 状态栏阻止全屏

    我的应用程序启动时可以全屏正确运行 然而 在最小化然后返回应用程序后 状态栏会弹出 并将我的视图向下推一点 如何防止状态栏移动我的视图 这是我的布局
  • 双屏 Android Studio 中不显示自动补全

    我刚刚从 Eclipse 切换到 Android Studio IntelliJ 我不明白自动补全是如何工作的 我尝试了 Control Space Control Shift Space 但没有相关建议 不在 Java 文件中 也不在布局
  • Android 操作项上的通知徽章

    我想在操作栏中放置的购物车图像上添加一个通知徽章 并以编程方式操作它 有帮助吗 您可以显示自定义MenuItem on ActionBar通过创建一个custom layout for MenuItem 要设置自定义布局 您必须使用菜单项属
  • 活动加载缓慢

    我的应用程序中有大约 50 个活动 但其中一个活动非常慢 我的意思是当我按下按钮进入该活动时 需要很长时间 有时会出现黑屏 在该活动中 我有一个按钮 如果我单击该按钮 则需要很长时间才能执行 代码很轻 只有一个Button EditText
  • 用于请求带有临时缓存的远程 Observable 的 RxJava 模式

    用例是这样的 我想暂时缓存最新发出的昂贵的Observable响应 但在它过期后 返回到昂贵的源Observable并再次缓存它 等等 一个非常基本的网络缓存场景 但我真的很难让它工作 private Observable
  • 如何从DataSource.Factory获取数据

    我必须调用此方法才能获取所有人员 我根本无法修改这个方法 Query SELECT FROM PERSON TABLE ORDER BY NAME DESC abstract fun getElements DataSource Facto

随机推荐

  • python的两种退出方式

    os exit vs sys exit 转自 xff1a http www cnblogs com gaott archive 2013 04 12 3016355 html 概述 python的程序有两种退出方式 xff1a os exi
  • R语言数据类型转化

    R语言数据类型转化 转自 xff1a http www wangluqing com 2014 09 10 r share34 有时候 xff0c 对于一些问题 xff0c 需要进行数据类型之间的转换 R提供了基本类型转换函数以解决数据类型
  • ubuntu20.04安装中文输入法

    虽然搜狗的官网已经宣传说已经支持2004 2010 xff0c 但是支持的并不完美 xff0c 闪退 xff0c 打不出字各种问题不断 xff0c 所以本文带领大家安装几款能够正常使用的中文输入法 但是正在我要发这篇博客的时候 xff0c
  • R语言做柱状图

    R语言做柱状图 转自 xff1a http www phperz com article 16 0102 180120 html 条形图代表在与条成比例的变量的值的长度矩形条数据 R使用函数barplot 来创建柱状图 R能够绘制柱状图垂直
  • R语言 PCA(主成分分析)

    R语言 PCA 转自 xff1a http www cnblogs com longzhongren p 4300593 html 1 关键点 综述 xff1a 主成分分析 因子分析 典型相关分析 xff0c 三种方法的共同点主要是用来对数
  • 使用Pandas对数据进行筛选和排序

    使用Pandas对数据进行筛选和排序 转自 xff1a http bluewhale cc 2016 08 06 use pandas filter and sort html 筛选和排序是Excel中使用频率最多的功能 xff0c 通过这
  • linux 下安装blat软件

    linux 下安装blat软件 blat是一款很经典的比对工具 xff0c 与blast相比 xff0c 具有速度快 共线性输出比对结果等优点 但是 xff0c blat源码包里面的README文件写得很不清楚 xff0c 这里 xff0c
  • 基于统计的压缩算法:游程编码

    原网址 xff1a http www cnblogs com xudong bupt p 3761417 html 基于统计的压缩算法 xff1a 游程编码 1 游程编码概念 游程编码又称 运行长度编码 或 行程编码 xff0c 是一种统计
  • BWT (Burrows–Wheeler_transform)数据转换算法

    原网址 xff1a https blog csdn net luanzheng 365 article details 78575429 BWT Burrows Wheeler transform 数据转换算法 1 什么是BWT 压缩技术主
  • pip使用豆瓣的镜像源

    抄自 xff1a https www cnblogs com ZhangRuoXu p 6370107 html pip使用豆瓣的镜像源 豆瓣镜像地址 xff1a https pypi douban com simple 虽然用easy i
  • PyVCF

    抄自 xff1a https www cnblogs com nkwy2012 p 9204088 html vcf文件的全称是variant call file xff0c 即突变识别文件 xff0c 它是基因组工作流程中产生的一种文件
  • 【Kotlin 初学者】扩展-享受编程

    作者简介 xff1a CSDN博客专家 华为云 云享专家认证 系列专栏 xff1a Kotlin 初学者 学习交流 xff1a 三人行必有我师焉 xff1b 择其善者而从之 xff0c 其不善者而改之 目录 一 介绍 二 扩展函数 2 1
  • 【Kotlin 初学者】函数式编程

    作者简介 xff1a CSDN博客专家 华为云 云享专家认证 系列专栏 xff1a Kotlin 初学者 五星好评 xff1a 左侧点一下 网页端 xff0c 移动端 xff1a https bbs csdn net topics 6039
  • centos8.5 更新失败

    今天使用yum makecache的时候出现了Error Failed to download metadata for repo 39 base 39 Cannot download repomd xml Cannot download
  • 【Kotlin 初学者】Java和Kotlin互操作

    作者简介 xff1a CSDN博客专家 华为云 云享专家认证 系列专栏 xff1a Kotlin 初学者 五星好评 xff1a 左侧点一下 网页端 xff0c 移动端 xff1a https bbs csdn net topics 6039
  • Kotlin 基础知识汇总(知识与实践相结合)

    2个月的时间总算把 Kotlin 的基础知识写完了 xff0c 下面咱们看看具体内容 xff1a 学习 Kotlin 的必要性 Kotlin 初学者 为什么要学Kotlin Kotlin 初学者 打牢基础的重要性 运行环境 Kotlin 初
  • HashMap的产生与原理

    一 HashMap的诞生 1 1 数组 数组 xff1a 一片物理上连续的大小确定的储存空间 好处 xff1a 根据下标快速的查找和修改里面的内容 缺点 xff1a 大小确定 xff0c 无法修改 添加新的元素或者删除元素比较麻烦 数组的静
  • Android 数据存储(一)-文件存储

    目录 一 数据存储概念 二 应用程序专属文件存储 2 1 访问持久文件 2 2 将数据存储到文件 2 3 从文件中读取数据 2 4 查看文件列表 2 5 删除文件 三 缓存文件 cache目录下 3 1 创建缓存文件 3 2 删除文件 四
  • 回顾2021,展望2022 | 年终总结

    你付出多少努力 xff0c 就必有多少收获 一 回顾 2021 2021 年输出109篇文章 xff0c 收获 xff1a 博客专家认证 Android领域新星创作者认证 博客之星Top50 同时也在问答模块解决了部分小伙伴的问题 xff0
  • Android 数据存储(二)-SP VS DataStore VS MMKV

    一 SharedPreferences 不同于文件的存储方式 xff0c 如果要保存的键值集合相对较小 xff0c 则应使用SharedReferences API SharedReferences对象指向一个包含键值对的文件 xff0c