Skip to content

android_advance

guoling edited this page Apr 3, 2026 · 23 revisions

MMKV for Android

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, Windows, POSIX and HarmonyOS NEXT.

Advanced Usage

There are some settings on MMKV that tune things to suit your need.

Log

  • By default, MMKV prints log to logcat, which is not convenient for diagnosing online issues. You can set up MMKV log redirecting on App startup. Implement interface MMKVHandler, and pass it during initialization:

    @Override
    public boolean wantLogRedirecting() {
        return true;
    }
    
    @Override
    public void mmkvLog(MMKVLogLevel level, String file, int line, String func, String message) {
        String log = "<" + file + ":" + line + "::" + func + "> " + message;
        switch (level) {
            case LevelDebug:
                //Log.d("redirect logging MMKV", log);
                break;
            case LevelInfo:
                //Log.i("redirect logging MMKV", log);
                break;
            case LevelWarning:
                //Log.w("redirect logging MMKV", log);
                break;
            case LevelError:
                //Log.e("redirect logging MMKV", log);
                break;
            case LevelNone:
                //Log.e("redirect logging MMKV", log);
                break;
        }
    }

    As for a logging tool, we recommend using xlog, which also comes from the WeChat team.

  • To register the handler, pass it during MMKV initialization:

    MMKV.initialize(this, null, null, MMKVLogLevel.LevelInfo, myHandler);
  • You can turn off MMKV's logging once and for all (which we strongly discourage).
    You should never turn MMKV's log off unless you have very strong evidence that it makes your App slow.

    MMKV.setLogLevel(MMKVLogLevel.LevelNone);

Encryption

  • By default MMKV stores all key values in plain text on file, relying on Android's sandbox to protect the file from unauthorized access. Should you worry about information leaking, you can choose to encrypt MMKV. MMKV supports both AES CFB-128 and AES CFB-256 encryption. By default AES-128 is used (keys are truncated to 16 bytes). To use AES-256, you must explicitly pass aes256 = true.

    String cryptKey = "My-Encrypt-Key";
    MMKV kv = MMKV.mmkvWithID("MyID", MMKV.SINGLE_PROCESS_MODE, cryptKey);
  • You can change the encryption key later as you like. You can also change an existing MMKV instance from encrypted to unencrypted, or vice versa.

    final String mmapID = "testAES_reKey1";
    // an unencrypted MMKV instance
    MMKV kv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, null);
    
    // change from unencrypted to encrypted with AES-128 key length
    kv.reKey("Key_seq_1");
    
    // change encryption key with AES-256 key length
    kv.reKey("Key_Seq_Very_Looooooooong", true);
    
    // change from encrypted to unencrypted
    kv.reKey(null);

Customize location

  • By default, MMKV stores file inside $(FilesDir)/mmkv/. You can customize MMKV's root directory on App startup:

    String dir = getFilesDir().getAbsolutePath() + "/mmkv_2";
    String rootDir = MMKV.initialize(this, dir);
    Log.i("MMKV", "mmkv root: " + rootDir);
  • You can even customize any MMKV instance's location:

    String relativePath = getFilesDir().getAbsolutePath() + "/mmkv_3";
    MMKV kv = MMKV.mmkvWithID("testCustomDir", relativePath);

    Note: It's recommended to store MMKV files inside your App's sandbox path. DO NOT store them on external storage(aka SD card). If you have to do it, you should follow Android's scoped storage enforcement.

Customize library loader

  • Some Android devices with API level 19 might have problems when installing/updating APK (relevant for v1.3.x LTS or earlier), aka missing libmmkv.so. As a result, you might face java.lang.UnsatisfiedLinkError crash. There's an open-source project ReLinker that fixes this problem. You can use it to load MMKV:

    String dir = getFilesDir().getAbsolutePath() + "/mmkv";
    MMKV.initialize(this, dir, new MMKV.LibLoader() {
        @Override
        public void loadLibrary(String libName) {
            ReLinker.loadLibrary(MyApplication.this, libName);
        }
    });

Native Buffer

  • Typically, when a String or byte[] value is getting from MMKV, there's a memory copying from native to JVM. And if that value is passed to another native library(JNI) immediately, another memory-copying from JVM to native happens. The whole process wastes too much if that value's size is large. Here comes the Native Buffer to the rescue.
    Native Buffer is a memory buffer created in native, wrapped as NativeBuffer in Java, which can be passed to another native library transparently. This process saves memory-copying to & from JVM. Example code:

    int sizeNeeded = kv.getValueActualSize("bytes");
    NativeBuffer nativeBuffer = MMKV.createNativeBuffer(sizeNeeded);
    if (nativeBuffer != null) {
        int size = kv.writeValueToNativeBuffer("bytes", nativeBuffer);
        Log.i("MMKV", "size Needed = " + sizeNeeded + " written size = " + size);
    
        // pass nativeBuffer to another native library
        // ...
    
        // destroy when you're done
        MMKV.destroyNativeBuffer(nativeBuffer);
    }

Recover from data corruption

  • By default, MMKV discards all data when there's a CRC check fail, or file length is not correct, which might happen on accidentally shutdown. You can tell MMKV to recover as much data as possible. The repair rate is not promised, though. And you might get unexpected key values from recovery. Implement interface MMKVHandler, add some code like these:

    @Override
    public MMKVRecoverStrategic onMMKVCRCCheckFail(String mmapID) {
        return MMKVRecoverStrategic.OnErrorRecover;
    }
    
    @Override
    public MMKVRecoverStrategic onMMKVFileLengthError(String mmapID) {
        return MMKVRecoverStrategic.OnErrorRecover;
    }

Content change notification

  • You can receive notifications when an MMKV instance's content is changed by another process, or when content is loaded successfully. Implement these MMKVHandler callbacks:

    @Override
    public boolean wantContentChangeNotification() {
        return true;
    }
    
    @Override
    public void onContentChangedByOuterProcess(String mmapID) {
        Log.i("MMKV", "content changed by other process: " + mmapID);
    }
    
    @Override
    public void onMMKVContentLoadSuccessfully(String mmapID) {
        Log.i("MMKV", "content loaded successfully: " + mmapID);
    }

    Note: MMKVContentChangeNotification is deprecated. Use MMKVHandler instead.

Backup & Restore

  • You can use MMKV's backup & restore API to backup your data to somewhere else, and restore them later.

    String backupRootDir = getFilesDir().getAbsolutePath() + "/mmkv_backup_3";
    // backup one instance
    boolean ret = MMKV.backupOneToDirectory(mmapID, backupRootDir, null);
    // backup all instances
    long count = MMKV.backupAllToDirectory(backupRootDir);
    
    // restore one instance
    ret = MMKV.restoreOneMMKVFromDirectory(mmapID, backupRootDir, otherDir);
    // restore all instances
    count = MMKV.restoreAllFromDirectory(backupRootDir);

Auto Expiration

  • Starting from v1.3.0, you can upgrade MMKV to auto key expiration. Note that this is a breaking change. Once upgraded to auto key expiration, the file is not valid for any older version of MMKV (<= v1.2.16) to operate correctly.

  • Global Expiration. The most simple way to do it is to turn on auto key expiration for all keys in the whole file.

    // expire in a day
    mmkv.enableAutoKeyExpire(MMKV.ExpireInDay); // MMKV.ExpireInDay = 24 * 60 * 60

    Or, if you prefer, you can enable auto key expiration without setting a global expiration duration. In this case, each key is not expired by default.

    // enable auto key expiration without global duration
    mmkv.enableAutoKeyExpire(MMKV.ExpireNever); // MMKV.ExpireNever = 0
  • Individual Expiration. You can set a special expiration duration for a key, regardless of whether the file has a global duration or not. Note that you have to enable auto key expiration first.

    // enable auto key expiration with an hour duration
    mmkv.enableAutoKeyExpire(MMKV.ExpireInHour); // MMKV.ExpireInHour = 60 * 60
    
    // set a key with the file's global expiration duration, aka MMKV.ExpireInHour
    mmkv.encode("key_1", "some value");
    
    // set a special key that expires in two hours
    mmkv.encode("key_2", "some value", 2 * 60 * 60);
    
    // set a special key that never expires
    mmkv.encode("key_3", "some value", MMKV.ExpireNever);

    Or, if you prefer, you can enable auto key expiration without setting a global expiration duration. In this case, each key is not expired by default.

    // enable auto key expiration without global duration
    mmkv.enableAutoKeyExpire(MMKV.ExpireNever); // MMKV.ExpireNever = 0
    
    // set a key that never expires
    mmkv.encode("key_1", "some value");
    
    // set a special key that expires in an hour
    mmkv.encode("key_2", "some value", MMKV.ExpireInHour);
  • The expire duration is counted in seconds. MMKV has some pre-defined duration for your convenience. You can use any other duration you prefer. For example, expiration in a week is 7 * 24 * 60 * 60.

    ExpireNever = 0;
    ExpireInMinute = 60;
    ExpireInHour = 60 * 60;
    ExpireInDay = 24 * 60 * 60;
    ExpireInMonth = 30 * 24 * 60 * 60;
    ExpireInYear = 365 * 30 * 24 * 60 * 60;
  • You can turn off auto key expiration:

    mmkv.disableAutoKeyExpire();

    Note: Once disabled, the file format stays upgraded. Old MMKV versions (<= v1.2.16) still cannot operate on it.

  • You can query non-expired keys:

    long nonExpiredCount = mmkv.countNonExpiredKeys();
    String[] nonExpiredKeys = mmkv.allNonExpiredKeys();

Instance Utility

  • MMKV provides several utility methods for inspecting and managing instances:

    MMKV mmkv = MMKV.defaultMMKV();
    
    // size info
    long totalSize = mmkv.totalSize();
    long actualSize = mmkv.actualSize();
    long keyCount = mmkv.count();
    
    // import from another instance
    MMKV other = MMKV.mmkvWithID("other");
    mmkv.importFrom(other);
    
    // manually sync to file
    mmkv.sync();
    mmkv.async();
    
    // reclaim disk space after heavy deletions
    mmkv.trim();
    
    // clear all with keeping file space (for reuse)
    mmkv.clearAllWithKeepingSpace();

File Management

  • You can check, validate, and remove MMKV storage files:

    // check if an MMKV file exists
    boolean exists = MMKV.checkExist("MyID");
    
    // validate file integrity
    boolean valid = MMKV.isFileValid("MyID");
    
    // remove MMKV storage files
    MMKV.removeStorage("MyID");

Read-Only Mode

  • Starting from v2.4.0, you can open an MMKV instance in read-only mode. Any write operation will be silently ignored.

    MMKV mmkv = MMKV.mmkvWithID("MyID", MMKV.READ_ONLY_MODE);
    // or combine with multi-process
    MMKV mmkv = MMKV.mmkvWithID("MyID", MMKV.MULTI_PROCESS_MODE | MMKV.READ_ONLY_MODE);

Compare Before Set

  • Starting from v2.4.0, you can turn on enableCompareBeforeSet() to reduce unnecessary write operations. When enabled, MMKV will compare the new value with the existing value before writing, and skip the write if they are the same.

    mmkv.enableCompareBeforeSet();

    Note: enableCompareBeforeSet() is NOT compatible with encryption or auto key expiration. Do not enable them on the same MMKV instance.

  • You can turn it off later:

    mmkv.disableCompareBeforeSet();

MMKVConfig

  • Starting from v2.4.0, MMKV provides an all-in-one config class MMKVConfig for convenient MMKV instance creation. It's a plain class with public fields — create one and set the fields you need.

    MMKVConfig config = new MMKVConfig();
    config.cryptKey = "My-Encrypt-Key";
    config.mode = MMKV.MULTI_PROCESS_MODE;
    MMKV kv = MMKV.mmkvWithID("MyID", config);
    
    // or use the default MMKV with config
    MMKVConfig defaultConfig = new MMKVConfig();
    MMKV defaultKV = MMKV.defaultMMKV(defaultConfig);

    Available fields (all have sensible defaults):

    Field Type Default Description
    mode int SINGLE_PROCESS_MODE Single or multi-process mode
    cryptKey String null Encryption key
    aes256 boolean false Use AES-256 key length
    rootPath String null Custom root directory
    expectedCapacity long 0 Initial file size hint
    enableKeyExpire Boolean null true/false to enable/disable auto key expiration
    expiredInSeconds int 0 Default expiration duration in seconds
    enableCompareBeforeSet boolean false Compare value before writing
    recover MMKVRecoverStrategic null Per-instance recovery strategy
    itemSizeLimit int 0 Max size per key-value pair (0 = unlimited)

NameSpace

Starting from v2.1.0, MMKV supports the NameSpace feature. It’s primarily designed to solve these problems:

  1. Using in other modules/3rd lib/SDK without initialization
    Traditionally, MMKV requires initialization before use, and it will cause problems in multiple libs because one can’t be sure if MMKV has been initialized, nor can be sure of the root directory of MMKV. With NameSpace, a lib can use MMKV without initialization, and of course without configuring global root directory.

  2. Fix the incorrect file naming in custom root directory
    Historically MMKV for Android mistakenly use mmapKey as mmapID, which will be problematic with the NameSpace feature. Starting from v2.1.0, MMKV will try to migrate them back to normal when possible.

  3. Make customizing root directory easy
    Using NameSpace you can customize the root directory of a group of MMKV instances easily.

  final String nsRoot = getFilesDir().getAbsolutePath() + "/mmkv_namespace";
  final NameSpace ns = MMKV.nameSpace(nsRoot);
  MMKV mmkv = ns.mmkvWithID("test_namespace");

  // get the default namespace (the global root directory)
  NameSpace defaultNS = MMKV.defaultNameSpace();
  • Backup & Restore within a NameSpace:

    // backup/restore within a namespace
    ns.backupOneToDirectory("test_namespace", dstPath);
    ns.restoreOneMMKVFromDirectory("test_namespace", dstPath);
  • Utility methods on NameSpace:

    boolean valid = ns.isFileValid("test_namespace");
    boolean exists = ns.checkExist("test_namespace");
    ns.removeStorage("test_namespace");

Key Notes:

  • This is a breaking change. It’s highly recommended that you upgrade to v2.0.2/v1.3.11+ first with forward support of normal MMKV in a custom directory.
  • Upgrade path: v2.0.2/v1.3.11+ → v2.1.0+ (make sure a smooth upgrade)

Using in C++ code

Starting from v2.1.0, MMKV’s C++ interface can be accessed in your C++ code. It's done by the Prefab functionality.
Steps to adapt:

  1. Use MMKV with share linking to libc++
    Change to "com.tencent.mmkv-shared” if you previously use “com.tencent.mmkv” or “com.tencent.mmkv-static”.

  2. Find and link MMKV in your CMake project

# Add these two lines
find_package(mmkv REQUIRED CONFIG)
target_link_libraries(your_awesome_lib mmkv::mmkv)
  1. Include MMKV header

    #include <MMKV/MMKV.h>
    auto ns = MMKV::nameSpace(nameSpaceDir);
    auto kv = ns.mmkvWithID("test_NDK");

Key Notes:

  • This feature requires Android Gradle Plugin 7.1+ with Prefab support, we recommend AGP 8+
  • For dynamic lib conflicts, force picking “com.tencent.mmkv-shared" via module...replacedBy...
  • Minimum NDK version required: r21 (compatible with C++17 standard), MMKV is built with this NDK version

What's Next

Clone this wiki locally