SharedPreferences的进化版-MMKV

49 篇文章 0 订阅
16 篇文章 0 订阅

什么是MMKV
MMKV的github地址:https://github.com/Tencent/MMKV

简介
MMKV is an efficient, small, easy-to-use mobile key-value storage framework used in the WeChat application. It’s currently available on Android, iOS/macOS, Win32 and POSIX.

官方介绍说MMKV是一套更有效率、更小、更易使用的移动端键值对存储框架,目前应用于微信App中,并且可以作为第三方框架用于Android、ios、win32和posix(Portable Operating System Interface可移植操作系统接口)系统。

特点
Efficient. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of Android to achieve best performance.
Multi-Process concurrency: MMKV supports concurrent read-read and read-write access between processes.
Easy-to-use. You can use MMKV as you go. All changes are saved immediately, no sync, no apply calls needed.
Small.
A handful of files: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It’s really tidy.
About 50K in binary size: MMKV adds about 50K per architecture on App size, and much less when zipped (apk).
因为使用的是mmap(一种内存映射文件方法)去保持内存和文件数据同步,并且使用protobuf编解码value,所以相较于Android传统的基于xml的SharedPreferences来说更有效率,并且支持多进程并发读写,这也相当于提高了效率。

相比于SP,所有的改变通过encode方法立即存入,不需要像SP调用apply存入内存,也不需要调用commit存入文件。

MMKV只包括进程锁、编解码的helper和mmap的逻辑,再没有其他的文件,在每个App中大约占50K,编译成apk的时候还会进一步压缩。

支持的类型
基本类型:boolean, int, long, float, double, byte[]
Classes & Collections:
String,Set
Any class that implements Parcelable 实现了Parcelable的自定义bean
使用
添加依赖
首先,作为第三方库的引入,当然是添加依赖:

dependencies {
    implementation 'com.tencent:mmkv-static:1.2.5'
    // replace "1.2.5" with any available version
}


初始化
初始化通常只在App启动的时候执行一次,所以放在继承自Application的自定义类下:

class MyApp extends Application{
  public void onCreate() {
      super.onCreate();

      String rootDir = MMKV.initialize(this);
      System.out.println("mmkv root: " + rootDir);
      //……
  }
}


注意这里的initialize方法有很多重载方法可以用:

public static String initialize(Context context) {
    String root = context.getFilesDir().getAbsolutePath() + "/mmkv";
    MMKVLogLevel logLevel = MMKVLogLevel.LevelInfo;
    return initialize(root, (MMKV.LibLoader)null, logLevel);
}

public static String initialize(Context context, MMKVLogLevel logLevel) {
    String root = context.getFilesDir().getAbsolutePath() + "/mmkv";
    return initialize(root, (MMKV.LibLoader)null, logLevel);
}

public static String initialize(String rootDir) {
    MMKVLogLevel logLevel = MMKVLogLevel.LevelInfo;
    return initialize(rootDir, (MMKV.LibLoader)null, logLevel);
}

public static String initialize(String rootDir, MMKVLogLevel logLevel) {
    return initialize(rootDir, (MMKV.LibLoader)null, logLevel);
}

public static String initialize(String rootDir, MMKV.LibLoader loader) {
    MMKVLogLevel logLevel = MMKVLogLevel.LevelInfo;
    return initialize(rootDir, loader, logLevel);
}

public static String initialize(String rootDir, MMKV.LibLoader loader, MMKVLogLevel logLevel) {
    if (loader != null) {
        if ("StaticCpp".equals("SharedCpp")) {
            loader.loadLibrary("c++_shared");
        }

        loader.loadLibrary("mmkv");
    } else {
        if ("StaticCpp".equals("SharedCpp")) {
            System.loadLibrary("c++_shared");
        }

        System.loadLibrary("mmkv");
    }

    jniInitialize(rootDir, logLevel2Int(logLevel));
    MMKV.rootDir = rootDir;
    return MMKV.rootDir;
}


最后都会回调到initialize(String rootDir, MMKV.LibLoader loader, MMKVLogLevel logLevel)这个方法。

第一个参数是MMKV保存数据文件的路径,默认路径是context.getFilesDir().getAbsolutePath() + “/mmkv”,你可以指定其他的合法路径作为存放地址;

通过第二个参数可以自定义MMKV库加载器:

public interface LibLoader {
    void loadLibrary(String var1);
}


通常我们不需要自定义,默认使用System的loadLibrary方法去加载MMKV类库。

第三个参数是设置MMKV输出Log的等级,默认info等级,无须多言。

经过类库加载,MMKV就被加载到了JVM中,jniInitialize方法是native方法,底层调用MMKV的c++方法进行初始化逻辑。存放、读取

import com.tencent.mmkv.MMKV;
...
MMKV kv = MMKV.defaultMMKV();

kv.encode("bool", true);
System.out.println("bool: " + kv.decodeBool("bool"));

kv.encode("int", Integer.MIN_VALUE);
System.out.println("int: " + kv.decodeInt("int"));

kv.encode("long", Long.MAX_VALUE);
System.out.println("long: " + kv.decodeLong("long"));

kv.encode("float", -3.14f);
System.out.println("float: " + kv.decodeFloat("float"));

kv.encode("double", Double.MIN_VALUE);
System.out.println("double: " + kv.decodeDouble("double"));

kv.encode("string", "Hello from mmkv");
System.out.println("string: " + kv.decodeString("string"));

byte[] bytes = {'m', 'm', 'k', 'v'};
kv.encode("bytes", bytes);
System.out.println("bytes: " + new String(kv.decodeBytes("bytes")));


encode方法是存储,第一个参数是key,第二个参数是value;

decodeXxx方法是读取,decode后面跟着要读取的value类型,参数是key。

移除、查询

MMKV kv = MMKV.defaultMMKV();

kv.removeValueForKey("bool");
System.out.println("bool: " + kv.decodeBool("bool"));

//批量删除
kv.removeValuesForKeys(new String[]{"int", "long"});
System.out.println("allKeys: " + Arrays.toString(kv.allKeys()));

//查看是否保存过某个key-value对
boolean hasBool = kv.containsKey("bool");


模块化
If different modules/logics need isolated storage, you can also create your own MMKV instance separately.

对于不同功能模块还可以创建针对性的mmkv对象:

MMKV mmkv = MMKV.mmkvWithID("MyID");
mmkv.encode("bool", true);


模式

MMKV mmkv = MMKV.mmkvWithID("InterProcessKV", MMKV.MULTI_PROCESS_MODE);
mmkv.encode("bool", true);


包括defaultMMKV方法,都可以设置是多线程模式还是单线程模式,默认是单线程模式。

导入SP
MMKV实现了SharedPreferences和SharedPreferences.Editor,所以你可以使用SP的api调用,比如:

public Editor putString(String key, @Nullable String value) {
    this.encodeString(this.nativeHandle, key, value);
    return this;
}


可见不需要commit和apply,当然你可以在后面加上commit或apply,但实际上没有任何意义,因为在put方法里已经调用了encodeXxx方法,之所以保留这些api是为了适配SP使用者的习惯。

MMKV里定义了导入SP的方法importFromSharedPreferences(SharedPreferences preferences):

public int importFromSharedPreferences(SharedPreferences preferences) {
    Map<String, ?> kvs = preferences.getAll();
    if (kvs != null && kvs.size() > 0) {
        Iterator var3 = kvs.entrySet().iterator();

        while(var3.hasNext()) {
            Entry<String, ?> entry = (Entry)var3.next();
            String key = (String)entry.getKey();
            Object value = entry.getValue();
            if (key != null && value != null) {
                if (value instanceof Boolean) {
                    this.encodeBool(this.nativeHandle, key, (Boolean)value);
                } else if (value instanceof Integer) {
                    this.encodeInt(this.nativeHandle, key, (Integer)value);
                } else if (value instanceof Long) {
                    this.encodeLong(this.nativeHandle, key, (Long)value);
                } else if (value instanceof Float) {
                    this.encodeFloat(this.nativeHandle, key, (Float)value);
                } else if (value instanceof Double) {
                    this.encodeDouble(this.nativeHandle, key, (Double)value);
                } else if (value instanceof String) {
                    this.encodeString(this.nativeHandle, key, (String)value);
                } else if (value instanceof Set) {
                    this.encode(key, (Set)value);
                } else {
                    simpleLog(MMKVLogLevel.LevelError, "unknown type: " + value.getClass());
                }
            }
        }

        return kvs.size();
    } else {
        return 0;
    }
}


所以如果项目中之前有过SP的存储,那么只需要调用一下这个方法就会把原先SP中的数据全部设置到MMKV中。

工具类封装
实际开发中,我们不需要每次都要从MMKV.defaultMMKV()这样开始,所以把MMKV的调用封装起来是一件重要的事情。

public class MySpUtils {

    private static MMKV mv = MMKV.defaultMMKV();;

    private MySpUtils() {}
  
    /**
     * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
     */
    public static void encode(String key, Object object) {
        if (object instanceof String) {
            mv.encode(key, (String) object);
        } else if (object instanceof Integer) {
            mv.encode(key, (Integer) object);
        } else if (object instanceof Boolean) {
            mv.encode(key, (Boolean) object);
        } else if (object instanceof Float) {
            mv.encode(key, (Float) object);
        } else if (object instanceof Long) {
            mv.encode(key, (Long) object);
        } else if (object instanceof Double) {
            mv.encode(key, (Double) object);
        } else if (object instanceof byte[] ) {
            mv.encode(key, (byte[]) object);
        } else {
            mv.encode(key, object.toString());
        }
    }

    public static void encodeSet(String key,Set<String> sets) {
        mv.encode(key, sets);
    }

    public static void encodeParcelable(String key,Parcelable obj) {
        mv.encode(key, obj);
    }

    /**
     * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值
     */
    public static Integer decodeInt(String key) {
        return mv.decodeInt(key, 0);
    }
    public static Double decodeDouble(String key) {
        return mv.decodeDouble(key, 0.00);
    }
    public static Long decodeLong(String key) {
        return mv.decodeLong(key, 0L);
    }
    public static Boolean decodeBoolean(String key) {
        return mv.decodeBool(key, false);
    }
    public static Float decodeFloat(String key) {
        return mv.decodeFloat(key, 0F);
    }
    public static byte[] decodeBytes(String key) {
        return mv.decodeBytes(key);
    }
    public static String decodeString(String key) {
        return mv.decodeString(key,"");
    }
    public static Set<String> decodeStringSet(String key) {
        return mv.decodeStringSet(key, Collections.<String>emptySet());
    }
    public static Parcelable decodeParcelable(String key) {
        return mv.decodeParcelable(key, null);
    }
    /**
     * 移除某个key对
     */
    public static void removeKey(String key) {
        mv.removeValueForKey(key);
    }
       /**
     * 移除部分key
     */
    public static void removeSomeKey(String[] keyArray) {
        mv.removeValuesForKeys(keyArray);
    }
    /**
     * 清除所有key
     */
    public static void clearAll() {
        mv.clearAll();
    }
      /**
       * 判断是否含有某个key
       */
      public static void hasKey(String key){
      return mv.containsKey(key);
    }
}


也可以根据需要添加各种定制化方法来设置线程模式、mmapId等。
————————————————
版权声明:本文为CSDN博主「Humble先生」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012504392/article/details/110244341

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值