SharedPreferences用于Android本地存储,使用较简单。SharedPreferences本身是一个接口,它的实现类是SharedPreferencesImpl,通过源码可以发现他的put和get方法都有synchronized锁,因此它是线程安全的。
SharedPreferences基本使用
存值
SharedPreferences sp = context.getSharedPreferences("sp_storage", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = getPreferences().edit();
editor.putString("name", "小明");
editor.apply();
// editor.commit();
apply和commit的区别
editor.apply()会先存入内存,后续才同步到磁盘,比commit效率高,特别是频繁存取的时候。
editor.commit()会直接写入磁盘,并且返回boolean表示是否写入成功,适合需要根据是否成功写入磁盘这个结果做后续操作的情景使用。
取值
SharedPreferences sp = context.getSharedPreferences("sp_storage", Context.MODE_PRIVATE);
sp.getString("name", "");
SharedPreferences封装
//封装成Util
public class SPHelper {
public static volatile SharedPreferences mSharedPreferences;
public static void set(String key, int value) {
SharedPreferences.Editor editor = getPreferences().edit();
editor.putInt(key, value);
editor.apply();
}
public static void set(String key, boolean value) {
SharedPreferences.Editor editor = getPreferences().edit();
editor.putBoolean(key, value);
editor.apply();
}
public static void set(String key, String value) {
SharedPreferences.Editor editor = getPreferences().edit();
editor.putString(key, value);
editor.apply();
}
public static void set(String key, long value) {
SharedPreferences.Editor editor = getPreferences().edit();
editor.putLong(key, value);
editor.apply();
}
public static boolean get(String key, boolean defValue) {
return getPreferences().getBoolean(key, defValue);
}
public static String get(String key, String defValue) {
return getPreferences().getString(key, defValue);
}
public static int get(String key, int defValue) {
return getPreferences().getInt(key, defValue);
}
public static long get(String key, long defValue) {
return getPreferences().getLong(key, defValue);
}
public static float get(String key, float defValue) {
return getPreferences().getFloat(key, defValue);
}
public static SharedPreferences getPreferences() {
if (mSharedPreferences == null) {
synchronized (SPHelper.class) {
if (mSharedPreferences == null) {
return BaseApplication.getContext().getSharedPreferences("sp_storage", Context.MODE_PRIVATE);
}
}
}
return mSharedPreferences;
}
}
//使用
//存
SPHelper.set("name", "小明");
//取
String name = SPHelper.get("name", "");
MMKV
MMKV是腾讯开发的比SharedPreferences更好用的库。
git地址
使用
添加依赖
dependencies {
implementation 'com.tencent:mmkv:1.2.12'
}
在Application中初始化
//Application下
public void onCreate() {
super.onCreate();
MMKV.initialize(this);
}
MMKV有一个全局的实例可以直接使用,MMKV.defaultMMKV()
MMKV kv = MMKV.defaultMMKV();//全局实例
MMKV kv = MMKV.mmkvWithID("MyID");//不同的ID下的MMKV以不同的存储区域保存键值对
常规使用
import com.tencent.mmkv.MMKV;
MMKV kv = MMKV.defaultMMKV();
//键值对存取
kv.encode("isUsed", true);
boolean isUsed = kv.decodeBool("isUsed");
kv.encode("age", 15);
int age = kv.decodeInt("age");
kv.encode("name", "ZhangFei");
String name = kv.decodeString("name");
System.out.println("isUsed="+isUsed+";age="+age+";name="+name);
//输出: isUsed=true;age=15;name=ZhangFei
//单个键值对删除
kv.removeValueForKey("isUsed");
//批量删除键值对
kv.removeValuesForKeys(new String[]{"isUsed", "age"});
//判断key对应的value是不是空
boolean hasBool = kv.containsKey("isUsed");
多进程中使用
MMKV kv = MMKV.mmkvWithID("InterProcessKV", MMKV.MULTI_PROCESS_MODE);
kv.encode("name", "XiaoMing");
适配SharedPreferences以及数据迁移
private void testImportSharedPreferences() {
MMKV preferences = MMKV.mmkvWithID("myData");
{
//将原来SharedPreferences的数据导入到MMKV
SharedPreferences old_man = getSharedPreferences("myData", MODE_PRIVATE);
preferences.importFromSharedPreferences(old_man);
//清空原来SharedPreferences中的数据
old_man.edit().clear().commit();
}
//这种写法是减少之前使用了SharedPreferences的相关代码改动,让之前editor相关的代码可以继续使用,并且不再需要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() is not needed any more
//editor.commit();
}
原理
MMKV是通过mmap内存映射文件来进行读写操作的,因此他的效率高于普通IO。采用Protobuf协议存储key-value结构,特点是基于二进制,比SharedPreferences使用的XML表示同样一段内容要短很多。采用crc校验确保数据的有效性。多进程实现需要处理写指针增长、内存重叠、内存增长的情况。
普通IO需要将文件内容从硬盘拷贝到内核空间的一个缓冲区,再将数据拷贝到用户空间,这个过程需要两次数据拷贝。而mmap将内核空间和用户空间的部分区域映射到同一块物理空间,那么把文件内容拷贝到内核空间的这块区域,相当于也拷贝到了用户空间。