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

简介: 一、SharedPreferences 不同于文件的存储方式,如果要保存的键值集合相对较小,则应使用SharedReferences API。SharedReferences对象指向一个包含键值对的文件,并提供简单的读写方法。 本文从SharedReferences开始逐步引入Preference、MMKV。

一、SharedPreferences


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


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


1.1 获取SharedPreferences对象


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


Context 类中的getSharedPreferences()方法


此方法接收两个参数:


  • 第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存放在/data/data//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


微信图片_20220525132445.png


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

微信图片_20220525132525.png


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

微信图片_20220525132602.png


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

微信图片_20220525132641.png


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


微信图片_20220525132658.png


一些简单的键值对存储可以使用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,在读取性能上也有相近或超越的表现。

微信图片_20220525132958.png


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


微信图片_20220525133031.png


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

微信图片_20220525133133.png微信图片_20220525133142.png


3.3.3 写入数据


1.    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


  • 任何 Parcelable 的类型


微信图片_20220525133307.png

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

微信图片_20220525133335.png


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

微信图片_20220525133406.png


3.3.6 添加数据扩展


  • 不同业务需要区别存储,可以单独创建自己的实例。
  • 业务需要多进程访问,那么在初始化的时候加上标志位 MMKV.MULTI_PROCESS_MODE

微信图片_20220525133431.png


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

微信图片_20220525133632.png


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数据库实例




相关文章
|
30天前
|
安全 Android开发 iOS开发
Android vs. iOS:构建生态差异与技术较量的深度剖析###
本文深入探讨了Android与iOS两大移动操作系统在构建生态系统上的差异,揭示了它们各自的技术优势及面临的挑战。通过对比分析两者的开放性、用户体验、安全性及市场策略,本文旨在揭示这些差异如何塑造了当今智能手机市场的竞争格局,为开发者和用户提供决策参考。 ###
|
4月前
|
安全 Android开发 iOS开发
Android vs iOS:哪个操作系统更适合你的智能设备?
在今天的移动设备市场上,Android和iOS是两个主要的操作系统选择。本文将分析它们的优缺点,帮助读者选择最适合自己需求的操作系统。 【7月更文挑战第6天】
91 2
|
21天前
|
搜索推荐 Android开发 iOS开发
安卓vs. iOS:操作系统的终极对决####
本文探讨了安卓和iOS两种主流移动操作系统的特点,从用户体验、系统生态、硬件兼容性等维度进行对比分析。通过深入浅出的方式,揭示了两者在技术层面的优劣及未来发展趋势,旨在帮助用户更好地理解并选择适合自己的平台。 ####
|
21天前
|
安全 搜索推荐 Android开发
Android vs. iOS:解锁智能手机操作系统的奥秘####
【10月更文挑战第21天】 在当今这个数字化时代,智能手机已成为我们生活中不可或缺的伙伴。本文旨在深入浅出地探讨两大主流操作系统——Android与iOS的核心差异、优势及未来趋势,帮助读者更好地理解这两个平台背后的技术哲学和用户体验设计。通过对比分析,揭示它们如何塑造了我们的数字生活方式,并展望未来可能的发展路径。无论您是技术爱好者还是普通用户,这篇文章都将带您走进一个充满创新与可能性的移动世界。 ####
40 3
|
22天前
|
安全 搜索推荐 Android开发
Android vs. iOS:一场永无止境的较量####
在智能手机操作系统领域,Android与iOS犹如两极,各自引领着不同的技术潮流和用户体验哲学。本文深入探讨了这两个平台的发展历程、核心优势、以及它们如何塑造了我们的数字生活,旨在为读者提供一个全面而客观的视角,理解这场持续多年的“战争”背后的真正意义。 ####
|
23天前
|
安全 Android开发 iOS开发
Android vs iOS:探索移动操作系统的设计与功能差异###
【10月更文挑战第20天】 本文深入分析了Android和iOS两个主流移动操作系统在设计哲学、用户体验、技术架构等方面的显著差异。通过对比,揭示了这两种系统各自的独特优势与局限性,并探讨了它们如何塑造了我们的数字生活方式。无论你是开发者还是普通用户,理解这些差异都有助于更好地选择和使用你的移动设备。 ###
45 3
|
24天前
|
搜索推荐 vr&ar Android开发
Android vs. iOS:一场永无止境的辩论#### 一、
在当今的智能手机市场,Android和iOS无疑是两大巨头,它们各有千秋,吸引了全球数十亿用户。本文将深入探讨这两个操作系统的核心差异、优势以及未来发展趋势,帮助读者更全面地理解这场科技界的“双雄争霸”。 #### 二、
|
5月前
|
IDE Android开发 iOS开发
Android VS iOS:哪个操作系统更适合开发者?**
**在移动应用开发领域,Android和iOS是两大主流操作系统,它们各自拥有独特的特点和优势。本文探讨了Android和iOS操作系统在开发者视角下的差异与优劣,分析了它们在开发环境、用户群体、市场前景等方面的比较,帮助开发者选择最适合他们需求的平台。
87 2
|
29天前
|
开发工具 Android开发 iOS开发
Android vs iOS:构建移动应用时的关键考量####
本文深入探讨了Android与iOS两大移动平台在开发环境、性能优化、用户体验设计及市场策略方面的差异性,旨在为开发者提供决策依据。通过对比分析,揭示两个平台各自的优势与挑战,帮助开发者根据项目需求做出更明智的选择。 ####
|
27天前
|
搜索推荐 Android开发 数据安全/隐私保护
安卓vs. iOS:两大操作系统的终极对决####
【10月更文挑战第17天】 本文将深入浅出地探讨安卓和iOS这两大智能手机操作系统的差异与优劣,通过对比它们的技术架构、用户体验、市场表现及未来发展趋势,为读者提供一个全面而客观的视角。无论你是技术爱好者还是普通消费者,都能从中获得有价值的信息。 ####
39 2