Initialize the

Before using the MMKV framework, you need to call the following methods for initialization

MMKV.initialize(context);
Copy the code

The Java layer here mainly obtains the path to save the file and passes it to the Native layer. The default path here is the MMKV path in the APP’s internal storage directory, which cannot be modified. If modification is required, clone the source code and manually modify and compile it.

public static String initialize(Context context) {
        String rootDir = context.getFilesDir().getAbsolutePath() + "/mmkv";
        initialize(rootDir);
        return rootDir;
    }
Copy the code

At the Native layer, jump to MMKV::initializeMMKV method by Java_com_tencent_mmkv_MMKV_initialize method, start a thread to initialize, and then check whether the internal path exists, if not, create it.

void MMKV::initializeMMKV(const std::string &rootDir) {
    static pthread_once_t once_control = PTHREAD_ONCE_INIT;
    pthread_once(&once_control, initialize);

    g_rootDir = rootDir;
    char *path = strdup(g_rootDir.c_str());
    mkPath(path);
    free(path);

    MMKVInfo("root dir: %s", g_rootDir.c_str());
}
Copy the code

Obtain the MMKV object

The methods for obtaining MMKV objects range from the dumbest defaultMMKV to the most complex mmkvWithAshmemID method, called on demand.

Public MMKV defaultMMKV (); Public MMKV defaultMMKV(int mode, String cryptKey); Public mmkvWithID(String mmapID); Public mmkvWithID(String mmapID, int mode); Public MMKV mmkvWithID(String mmapID, int mode, String cryptKey); @nullable public MMKV mmkvWithAshmemID(Context Context, String mmapID, int size, int mode, String cryptKey);Copy the code

The above method basically goes to getMMKVWithID and then jumps to MMKV::mmkvWithID

MMKV *MMKV::mmkvWithID(const std::string &mmapID, int size, MMKVMode mode, string *cryptKey) {
    if (mmapID.empty()) {
        return nullptr;
    }
    SCOPEDLOCK(g_instanceLock);

    auto itr = g_instanceDic->find(mmapID);
    if(itr ! = g_instanceDic->end()) { MMKV *kv = itr->second;return kv;
    }
    auto kv = new MMKV(mmapID, size, mode, cryptKey);
    (*g_instanceDic)[mmapID] = kv;
    return kv;
}
Copy the code

G_instanceDic is a Map object. It is first searched in g_instanceDic based on mmapID, and then returned directly, without creating an MMKV object, and then added to g_instanceDic.

MMKV::MMKV(const std::string &mmapID, int size, MMKVMode mode, string *cryptKey) : m_mmapID(mmapID) , m_path(mappedKVPathWithID(m_mmapID, mode)) , m_crcPath(crcPathWithID(m_mmapID, mode)) , m_metaFile(m_crcPath, DEFAULT_MMAP_SIZE, (mode & MMKV_ASHMEM) ? MMAP_ASHMEM : MMAP_FILE) , m_crypter(nullptr) , m_fileLock(m_metaFile.getFd()) , m_sharedProcessLock(&m_fileLock, SharedLockType) , m_exclusiveProcessLock(&m_fileLock, ExclusiveLockType) , m_isInterProcess((mode & MMKV_MULTI_PROCESS) ! = 0) , m_isAshmem((mode & MMKV_ASHMEM) ! = 0) { m_fd = -1; m_ptr = nullptr; m_size = 0; m_actualSize = 0; m_output = nullptr;if (m_isAshmem) {
        m_ashmemFile = new MmapedFile(m_mmapID, static_cast<size_t>(size), MMAP_ASHMEM);
        m_fd = m_ashmemFile->getFd();
    } else {
        m_ashmemFile = nullptr;
    }

    if (cryptKey && cryptKey->length() > 0) {
        m_crypter = new AESCrypt((const unsigned char *) cryptKey->data(), cryptKey->length());
    }

    m_needLoadFromFile = true; m_crcDigest = 0; m_sharedProcessLock.m_enable = m_isInterProcess; m_exclusiveProcessLock.m_enable = m_isInterProcess; // sensitive zone { SCOPEDLOCK(m_sharedProcessLock); loadFromFile(); }}Copy the code

In the constructor of MMKV, a series of parameters are constructed, respectively:

  • M_mmapID: indicates the file name
  • M_path: indicates the storage path
  • M_crcPath: indicates the path where the verification file is stored
  • M_metaFile: managed object of memory mapping
  • M_crypter: AES encryption key
  • M_lock: thread lock
  • M_fileLock: file lock
  • M_sharedProcessLock: Lock that maps files to memory
  • M_exclusiveProcessLock: Lock when reading or writing data to memory
  • M_isInterProcess: indicates whether the process is active
  • M_isAshmem: indicates whether the memory is anonymous
  • M_ptr: address of the file mapped to memory
  • M_size: indicates the file size
  • M_actualSize: Memory size, which changes dynamically as data is written
  • M_output: Protobuf object, used to write files, is also critical for efficiency
  • M_ashmemFile: File object of anonymous memory
  • M_needLoadFromFile: an object identifying whether a file has been loaded, set to false if so
  • M_crcDigest: data verification


public static MMKV mmkvWithID(String mmapID, int mode, String cryptKey) {
    long handle = getMMKVWithID(mmapID, mode, cryptKey);
    return new MMKV(handle);
}
Copy the code

Write the data

Take writing to a String as an example

public boolean encode(String key, String value) {
    return encodeString(nativeHandle, key, value);
}
Copy the code

Go to MMKV::setStringForKey method

bool MMKV::setStringForKey(const std::string &value, const std::string &key) {
    if (key.empty()) {
        return false;
    }
    auto data = MiniPBCoder::encodeDataWithObject(value);
    return setDataForKey(std::move(data), key);
}
Copy the code

MiniPBCoder: : encodeDataWithObject method will be the value construct a Protobuf data objects (without detailed analysis of this chapter), and then to construct a data object through the STD: : move method to setDataForKey

bool MMKV::setDataForKey(MMBuffer &&data, const std::string &key) {
    if (data.length() == 0 || key.empty()) {
        return false;
    }
    SCOPEDLOCK(m_lock);
    SCOPEDLOCK(m_exclusiveProcessLock);
    checkLoadData();

    // m_dic[key] = std::move(data);
    auto itr = m_dic.find(key);
    if (itr == m_dic.end()) {
        itr = m_dic.emplace(key, std::move(data)).first;
    } else {
        itr->second = std::move(data);
    }

    return appendDataWithKey(itr->second, key);
}
Copy the code
  • CheckLoadData () is used to check the validity of files (this chapter does not examine this in detail)
  • M_dic is a Map object that checks whether the Key already exists. If it exists, replace it. If it does not, add it
  • AppendDataWithKey () adds the object to memory (this chapter does not go into detail)

Read the data

public String decodeString(String key, String defaultValue) {
    return decodeString(nativeHandle, key, defaultValue);
}
Copy the code

Go to the MMKV::getDataForKey method

const MMBuffer &MMKV::getDataForKey(const std::string &key) {
    SCOPEDLOCK(m_lock);
    checkLoadData();
    auto itr = m_dic.find(key);
    if(itr ! = m_dic.end()) {return itr->second;
    }
    static MMBuffer nan(0);
    return nan;
}
Copy the code

Search the m_dic object by key, return if found, return a 0 length object if not

At this point, the overall process is covered, and there are a lot of primitive things, which will be covered in later chapters, including mmap mapping operations, and protobuf reading and writing principles, etc. If this article is useful to you, please give it a like!