号外!腾讯MMKV也支持鸿蒙Next了

557 阅读5分钟

腾讯MMKV作为替代SharedPreference的存在,在安卓和IOS中已经广泛使用。它主要有以下特点: 高效:基于mmap和protobuf 存取key-value 键值对,不传统的方法性能更好,并且支持多线程,多进程同步数据。 使用简单:不需要sync()和flush(),仅需应用启动的时候 MMKV.initialize(appCtx),就可以在应用任何地方实时存取数据。

鸿蒙MMKV 使用教程

Prerequisites

  • Apps using MMKV can target: HarmonyOS Next (3.0.0.13) or later.
  • ARM64 & x86_64 architecture.
  • DevEco Studio NEXT Developer Beta1 (5.0.3.100) or later.

Installation

Via OHPM (recommended):

This is the fastest and most recommended way to add MMKV to your project.

ohpm install @tencent/mmkv

Or, you can add it to your project manually.

  • Add the following lines to oh-package.json5 on your app module.

    "dependencies": {
        "@tencent/mmkv": "1.3.9",
    }
    
  • Then run

    ohpm install
    

Via Source:

You can integrate MMKV by source.

  • Checkout MMKV source code:

    git clone https://github.com/Tencent/MMKV.git
    

    Note:

    • you may consider adding MMKV as git submodule.
    • By default, MMKV links libc++ statically. If you want smaller binary size and you know what you're doing, you can edit the file build-profile.json5 of the MMKV module, change the line to "arguments": "-DOHOS_STL=c++_shared", and "cppFlags": "-DMMKV_STL_SHARED=1.
    • If you want to access MMKV's C++ interface directly, you can edit the file build-profile.json5 of the MMKV module, changing the line to "cppFlags": "…… -fvisibility=default ……". Note that this will increase the binary size a little bit.
  • Add the following lines to oh-package.json5 on your app module.

    "dependencies": {
        "@tencent/mmkv": "file:path/to/mmkv/OpenHarmony/MMKV",
    }
    
  • Then run

    ohpm install
    

Via Pre-Built HAR (not recommended):

You can integrate MMKV by pre-built HAR.

  • Checkout MMKV source code:

    git clone https://github.com/Tencent/MMKV.git
    
  • Open the MMKV project in path/to/mmkv/OpenHarmony, open the Product window and select Build Mode to "release"; Select MMKV module, in the menu select Build -> Make Module 'MMKV', export the build result as "MMKV.har" to your project folder.

    • you may consider adding MMKV as git submodule.
    • By default, MMKV links libc++ statically. If you want smaller binary size and you know what you're doing, you can edit the file build-profile.json5 of the MMKV module, change the line to "arguments": "-DOHOS_STL=c++_shared", and "cppFlags": "-DMMKV_STL_SHARED=1.
    • If you want to access MMKV's C++ interface directly, you can edit the file build-profile.json5 of the MMKV module, changing the line to "cppFlags": "…… -fvisibility=default ……". Note that this will increase the binary size a little bit.
  • Add the following lines to oh-package.json5 on your app module.

    "dependencies": {
        "@tencent/mmkv": "file:path/to/MMKV.har",
    }
    
  • Then run

    ohpm install
    

Setup

You can use MMKV as you go. All changes are saved immediately, no sync, no apply calls needed.
Setup MMKV on App startup, say your EntryAbility.onCreate() function, add these lines:

import { MMKV } from '@tencent/mmkv';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    let appCtx = this.context.getApplicationContext();
    let mmkvRootDir = MMKV.initialize(appCtx);
    console.info('mmkv rootDir: ', mmkvRootDir);
    ……
  }

CRUD Operations

  • MMKV has a global instance, that can be used directly:

    import { MMKV } from '@tencent/mmkv';
        
    let mmkv = MMKV.defaultMMKV();
    mmkv.encodeBool('bool', true);
    console.info('bool = ', mmkv.decodeBool('bool'));
    
    mmkv.encodeInt32('int32', Math.pow(2, 31) - 1);
    console.info('max int32 = ', mmkv.decodeInt32('int32'));
    
    mmkv.encodeInt64('int', BigInt(2**63) - BigInt(1));
    console.info('max int64 = ', mmkv.decodeInt64('int'));
    
    let str: string = 'Hello OpenHarmony from MMKV';
    mmkv.encodeString('string', str);
    console.info('string = ', mmkv.decodeString('string'));
    
    let arrayBuffer: ArrayBuffer = StringToArrayBuffer('Hello OpenHarmony from MMKV with bytes');
    mmkv.encodeBytes('bytes', arrayBuffer);
    let bytes = mmkv.decodeBytes('bytes');
    console.info('bytes = ', ArrayBufferToString(bytes));
    

    As you can see, MMKV is quite easy to use.

  • Deleting & Querying:

    mmkv.removeValueForKey('bool');
    console.info('contains "bool"', mmkv.containsKey('bool'));
    
    mmkv.removeValuesForKeys(['int32', 'int']);
    console.info('all keys: ', mmkv.allKeys().join());
    
  • If different modules/logic need isolated storage, you can also create your own MMKV instance separately:

    var mmkv = MMKV.mmkvWithID('test');
    mmkv.encodeBool('bool', true);
    console.info('bool = ', mmkv.decodeBool('bool'));
    
  • If multi-process accessing is needed,you can set MMKV.MULTI_PROCESS_MODE on MMKV initialization:

    var mmkv = MMKV.mmkvWithID('test-multi-process', MMKV.MULTI_PROCESS_MODE);
    mmkv.encodeBool('bool', true);
    console.info('bool = ', mmkv.decodeBool('bool'));
    

Supported Types

  • Primitive Types:

    • boolean, number, bigint, string
  • Classes & Collections:

    • boolean[], number[], string[], ArrayBuffer
  • Custom Classes:
    We don't support custom classes directly because there's no Parcel replacement in OHOS. You can encode/decode to JSON first. Or use whatever encoding lib you like such as protobuf.

    let user = {
      username: 'dummy_user',
      age: 23
    }
    
    // Serialize the object into a JSON string
    mmkv.encodeString('user', JSON.stringify(user))
    
    // Deserialize the JSON string into an object
    let jsonUser = mmkv.decodeString('user') // { 'username': 'dummy_user', 'age': 23 }
    let userObject = JSON.parse(jsonUser)
    

Log

  • By default, MMKV prints log to hilog, which is not convenient for diagnosing online issues. You can setup MMKV log redirecting on App startup on the native interface of MMKV. Checkout how to do it on C++. Due to the current limitation of NAPI runtime, we can't efficiently redirect log to the JavaScript side.

  • You can turn off MMKV's logging once and for all on initialization (which we strongly disrecommend).

    import { MMKV, MMKVLogLevel } from '@tencent/mmkv';
    
    MMKV.initialize(appCtx, MMKVLogLevel.None);
    

Encryption

  • By default MMKV stores all key-values in plain text on file, relying on Android's/iOS's sandbox to make sure the file is encrypted. Should you worry about information leaking, you can choose to encrypt MMKV.

    let encryptKey = 'MyEncryptKey';
    let mmkv = MMKV.mmkvWithID('test-encryption', MMKV.SINGLE_PROCESS_MODE, encryptKey);
    
  • 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.

    // an unencrypted MMKV instance
    let mmkv = MMKV.mmkvWithID('test-encryption');
    
    // change from unencrypted to encrypted
    mmkv.reKey('Key_seq_1');
    
    // change encryption key
    mmkv.reKey('Key_seq_2');
    
    // change from encrypted to unencrypted
    kmmkv.reKey();
    

Customize location

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

    let appCtx = this.context.getApplicationContext();
    let rootDir = appCtx.filesDir + '/mmkv_2';
    let cacheDir = appCtx.cacheDir;
    MMKV.initializeWithPath(rootDir, cacheDir);
    
  • You can even customize any MMKV instance's location:

    let appCtx = this.context.getApplicationContext();
    let rootDir = appCtx.filesDir + '/mmkv_3';
    var mmkv = MMKV.mmkvWithID('testCustomDir', MMKV.SINGLE_PROCESS_MODE, null, rootDir);
    

    Note:  It's recommended to store MMKV files inside your App's sandbox path. DO NOT store them on external storage(aka SD card).

Native Buffer

  • Typically, when a string or ArrayBuffer value is getting from MMKV, there's a memory copying from native to JSVM. And if that value is passed to another native library(NAPI) immediately, another memory-copying from JSVM 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 JavaScript, which can be passed to another native library transparently. This process saves memory-copying to & from JSVM. Example code:

    let sizeNeeded = mmkv.getValueSize('bytes', true);
    let nativeBuffer = MMKV.createNativeBuffer(sizeNeeded);
    if (nativeBuffer != null) {
        let size = mmkv.writeValueToNativeBuffer('bytes', nativeBuffer);
        console.info('NativeBuffer: size Needed = ', sizeNeeded, ',  written size = ', size);
    
        // pass nativeBuffer to another native library
        // ...
    
        // destroy when you're done
        MMKV.destroyNativeBuffer(nativeBuffer);
    }
    

Backup & Restore

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

    let rootDir = ...;
    let backupRootDir = rootDir + '/mmkv_backup';
    // backup one instance
    let ret = MMKV.backupOneToDirectory(mmapID, backupRootDir);
    // backup all instances
    let count = MMKV.backupAllToDirectory(backupRootDir);
    
    // restore one instance
    ret = MMKV.restoreOneMMKVFromDirectory(mmapID, backupRootDir);
    // restore all instances
    count = MMKV.restoreAllFromDirectory(backupRootDir);
    

Auto Expiration

  • 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 with 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.encodeString('key_1', 'some value');
    
    // set a special key that expires in two hours
    mmkv.encodeString('key_2', 'some value', 2 * 60 * 60);
    
    // set a special key that never expires
    mmkv.encodeString('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.encodeString('key_1', 'some value');
    
    // set a special key that expires in an hour
    mmkv.encodeString('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.

    static const int ExpireNever = 0;
    static const int ExpireInMinute = 60;
    static const int ExpireInHour = 60 * 60;
    static const int ExpireInDay = 24 * 60 * 60;
    static const int ExpireInMonth = 30 * 24 * 60 * 60;
    static const int ExpireInYear = 365 * 30 * 24 * 60 * 60;