大家好,我系苍王。 以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。

[Android]如何做一个崩溃率少于千分之三噶应用app--章节列表

组件化群1已经满员,进来的可以加群2 763094035

MMKV框架初始化

    MMKV.initialize(this);    public static String initialize(Context context) {        //创建存储目录        rootDir = context.getFilesDir().getAbsolutePath() + "/mmkv";        initialize(rootDir);        return rootDir;    }    private static native void initialize(String var0);复制代码

之后是调用jni中的操作了

extern "C" JNIEXPORT JNICALL voidJava_com_tencent_mmkv_MMKV_initialize(JNIEnv *env, jobject obj, jstring rootDir) {    //c++中大于0和非空都是为真     if (!rootDir) {        return;    }    //string转为char*    const char *kstr = env->GetStringUTFChars(rootDir, nullptr);    if (kstr) {        MMKV::initializeMMKV(kstr);        //初始化完后释放kstr        env->ReleaseStringUTFChars(rootDir, kstr);    }}复制代码

MMKV初始化, pthread_once()函数详解

在多线程环境中,有些事仅需要执行一次。通常当初始化应用程序时,可以比较容易地将其放在main函数中。但当你写一个库时,就不能在main里面初始化了,你可以用静态初始化,但使用一次初始化(pthread_once)会比较容易些。

int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));

功能:本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。

在多线程编程环境下,尽管pthread_once()调用会出现在多个线程中,init_routine()函数仅执行一次,究竟在哪个线程中执行是不定的,是由内核调度来决定。 那么initialize方法只会执行一次。

void MMKV::initializeMMKV(const std::string &rootDir) {    //进程控制    static pthread_once_t once_control = PTHREAD_ONCE_INIT;    //进程中只执行一次initialize    pthread_once(&once_control, initialize);    //保存文件地址    g_rootDir = rootDir;    //strdup字符串拷贝    //c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同.这是为了与c语言兼容,在c语言中没有string类型,故必须通过string类对象的成员函数c_str()把string 对象转换成c中的字符串样式。注意:一定要使用strcpy()函数 等来操作方法c_str()返回的指针    //需要分配空间的    char *path = strdup(g_rootDir.c_str());    //创建MmapedFile    mkPath(path);    //清除地址空间    free(path);    MMKVInfo("root dir: %s", g_rootDir.c_str());}void initialize() {     //建立哈希表   // 使用unordered_map优点因为内部实现了哈希表,因此其查找速度非常的快 ,缺点哈希表的建立比较耗费时间    g_instanceDic = new unordered_map;    //初始化线程锁    g_instanceLock = ThreadLock();    //testAESCrypt();    MMKVInfo("page size:%d", DEFAULT_MMAP_SIZE);}//使用getpagesize函数获得一页内存大小//系统给我们提供真正的内存时,用页为单位提供,一次最少提供一页的真实内存空间 //分配内存空间:你真实的分配了多少内存,就使用多少内存,不要越界使用 //但是系统提供的真实内存空间是以页来提供的。const int DEFAULT_MMAP_SIZE = getpagesize();复制代码

创建保存的文件夹,遍历地址名,然后循环创建

bool mkPath(char *path) {    //文件(夹)信息结构体    struct stat sb = {};    bool done = false;    char *slash = path;    while (!done) {        //拿出文件夹名        slash += strspn(slash, "/");        slash += strcspn(slash, "/");        //遍历到最尾部        done = (*slash == '\0');        *slash = '\0';        //如果文件夹不为空        if (stat(path, &sb) != 0) {            //创建文件夹            if (errno != ENOENT || mkdir(path, 0777) != 0) {                MMKVWarning("%s", path);                return false;            }        } else if (!S_ISDIR(sb.st_mode)) {  //如果不是文件夹,抛错误            MMKVWarning("%s: %s", path, strerror(ENOTDIR));            return false;        }        *slash = '/';    }    return true;}复制代码

保存文件初始化

    //需要填写mmapId,线程(单线程,多线程),加密密钥    MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, cryptKey);    public static MMKV mmkvWithID(String mmapID, int mode, String cryptKey) {        if(rootDir == null) {  //如果主目录未初始化抛出异常            throw new IllegalStateException("You should Call MMKV.initialize() first.");        } else {            //id 不能超过46个字节            verifyMMID(mmapID);             创立出jni中分配的ID            long handle = getMMKVWithID(mmapID, mode, cryptKey);            return new MMKV(handle);        }    }    //mmkv的id是long类型    private static native long getMMKVWithID(String var0, int var1, String var2);复制代码
extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_getMMKVWithID(    JNIEnv *env, jobject obj, jstring mmapID, jint mode, jstring cryptKey) {    MMKV *kv = nullptr;  //初始化为空指针    if (!mmapID) {  //如果为空,直接返回空指针        return (jlong) kv;      }    string str = jstring2string(env, mmapID);  //格式化mmapId    if (cryptKey != nullptr) {        string crypt = jstring2string(env, cryptKey);  //格式化密钥        if (crypt.length() > 0) {             //创建文件,大小为一页,和加密密钥            kv = MMKV::mmkvWithID(str, DEFAULT_MMAP_SIZE, (MMKVMode) mode, &crypt);        }    }    if (!kv) {  //如果创建失败,重新创建一个不加密的文件        kv = MMKV::mmkvWithID(str, DEFAULT_MMAP_SIZE, (MMKVMode) mode, nullptr);    }    return (jlong) kv;}MMKV *MMKV::mmkvWithID(const std::string &mmapID, int size, MMKVMode mode, string *cryptKey) {    //如果mmapId为空范围空指针    if (mmapID.empty()) {        return nullptr;    }    //设定单例锁    SCOPEDLOCK(g_instanceLock);    //在记录文件的map中找mampID,对应的mapFile文件    auto itr = g_instanceDic->find(mmapID);    //返回结束位置胡迭代器    //如果已经存在    if (itr != g_instanceDic->end()) {        //返回现在对应mmkv        MMKV *kv = itr->second;        return kv;    }    //新建一个mmkv    auto kv = new MMKV(mmapID, size, mode, cryptKey);     //保存到map中    (*g_instanceDic)[mmapID] = kv;    return kv;}复制代码

创建MMKV 初始化

MMKV::MMKV(const std::string &mmapID, int size, MMKVMode mode, string *cryptKey)    : m_mmapID(mmapID)    , m_path(mappedKVPathWithID(m_mmapID, mode)) //构建mmkv文件地址    , m_crcPath(crcPathWithID(m_mmapID, mode))  //crc文件    , m_metaFile(m_crcPath, DEFAULT_MMAP_SIZE, (mode & MMKV_ASHMEM) ? MMAP_ASHMEM : MMAP_FILE) //初始化MmapedFile    , 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) { //如果需要共享内存,需要使用创建共享共享内存的MmapedFile        m_ashmemFile = new MmapedFile(m_mmapID, static_cast(size), MMAP_ASHMEM);        //读取匿名内存的文件fd地址        m_fd = m_ashmemFile->getFd();    } else {        m_ashmemFile = nullptr;    }    if (cryptKey && cryptKey->length() > 0) { //是否存在加密密钥,存在则创建AES加密器        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();    }}复制代码

读取文件

void MMKV::loadFromFile() {    if (m_isAshmem) {  //是否使用匿名内存,是则初始化匿名内存        loadFromAshmem();        return;    }    m_metaInfo.read(m_metaFile.getMemory());   //打开文件,m_fd文件描述符    m_fd = open(m_path.c_str(), O_RDWR | O_CREAT, S_IRWXU);      if (m_fd < 0) {  //文件不存在        MMKVError("fail to open:%s, %s", m_path.c_str(), strerror(errno));    } else {        m_size = 0;        struct stat st = {0};        if (fstat(m_fd, &st) != -1) { //文件状态是否可读            m_size = static_cast(st.st_size);        }        // round up to (n * pagesize)        if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) { //文件大小有内容,且小于1页            size_t oldSize = m_size;            m_size = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;            if (ftruncate(m_fd, m_size) != 0) {  //指定文件大小输出是否可行                MMKVError("fail to truncate [%s] to size %zu, %s", m_mmapID.c_str(), m_size,                          strerror(errno));                m_size = static_cast(st.st_size);            }            zeroFillFile(m_fd, oldSize, m_size - oldSize); //清空文件        }         //将文件映射到内存        m_ptr = (char *) mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);        //之后操作和匿名内存中操作相同        if (m_ptr == MAP_FAILED) {            MMKVError("fail to mmap [%s], %s", m_mmapID.c_str(), strerror(errno));        } else {            memcpy(&m_actualSize, m_ptr, Fixed32Size);            MMKVInfo("loading [%s] with %zu size in total, file size is %zu", m_mmapID.c_str(),                     m_actualSize, m_size);            bool loaded = false;            if (m_actualSize > 0) {                if (m_actualSize < m_size && m_actualSize + Fixed32Size <= m_size) {                    if (checkFileCRCValid()) {                        MMKVInfo("loading [%s] with crc %u sequence %u", m_mmapID.c_str(),                                 m_metaInfo.m_crcDigest, m_metaInfo.m_sequence);                        MMBuffer inputBuffer(m_ptr + Fixed32Size, m_actualSize, MMBufferNoCopy);                        if (m_crypter) {                            decryptBuffer(*m_crypter, inputBuffer);                        }                        //初始化Photobuf存储结构orderedMap                        m_dic = MiniPBCoder::decodeMap(inputBuffer);                        m_output = new CodedOutputData(m_ptr + Fixed32Size + m_actualSize,                                                       m_size - Fixed32Size - m_actualSize);                        loaded = true;                    }                }            }            if (!loaded) {                SCOPEDLOCK(m_exclusiveProcessLock);                if (m_actualSize > 0) {                    writeAcutalSize(0);                }                m_output = new CodedOutputData(m_ptr + Fixed32Size, m_size - Fixed32Size);                recaculateCRCDigest();            }            MMKVInfo("loaded [%s] with %zu values", m_mmapID.c_str(), m_dic.size());        }    }    if (!isFileValid()) {        MMKVWarning("[%s] file not valid", m_mmapID.c_str());    }    m_needLoadFromFile = false;}复制代码

读取匿名内存

void MMKV::loadFromAshmem() {    //复制MmapedFile的段地址    m_metaInfo.read(m_metaFile.getMemory());    //如果匿名内存存在且匿名内存文件为真    if (m_fd < 0 || !m_ashmemFile) {        MMKVError("ashmem file invalid %s, fd:%d", m_path.c_str(), m_fd);    } else {        //读取匿名内存文件大小        m_size = m_ashmemFile->getFileSize();        //读取匿名内存起始地址        m_ptr = (char *) m_ashmemFile->getMemory();        if (m_ptr != MAP_FAILED) {  //如果地址存在            //复制匿名内存出来            memcpy(&m_actualSize, m_ptr, Fixed32Size);            MMKVInfo("loading [%s] with %zu size in total, file size is %zu", m_mmapID.c_str(),                     m_actualSize, m_size);            bool loaded = false;            if (m_actualSize > 0) {                if (m_actualSize < m_size && m_actualSize + Fixed32Size <= m_size) {                    if (checkFileCRCValid()) {//检查m_ptr和m_size都设置了                        MMKVInfo("loading [%s] with crc %u sequence %u", m_mmapID.c_str(),                                 m_metaInfo.m_crcDigest, m_metaInfo.m_sequence);                        //创造MMBuffer数据                                        MMBuffer inputBuffer(m_ptr + Fixed32Size, m_actualSize, MMBufferNoCopy);                        if (m_crypter) { //如果加密,解密MMBuffer数据                            decryptBuffer(*m_crypter, inputBuffer);                        }                        //                                       m_dic = MiniPBCoder::decodeMap(inputBuffer);                        m_output = new CodedOutputData(m_ptr + Fixed32Size + m_actualSize,                                                       m_size - Fixed32Size - m_actualSize);                       //标志已经读取成功                        loaded = true;                    }                }            }            if (!loaded) {                 //使用范围锁                SCOPEDLOCK(m_exclusiveProcessLock);                if (m_actualSize > 0) {                    writeAcutalSize(0);                }                //输出流                m_output = new CodedOutputData(m_ptr + Fixed32Size, m_size - Fixed32Size);                //重新计算crc校验                recaculateCRCDigest();            }            MMKVInfo("loaded [%s] with %zu values", m_mmapID.c_str(), m_dic.size());        }    }    if (!isFileValid()) {        MMKVWarning("[%s] ashmem not valid", m_mmapID.c_str());    }    m_needLoadFromFile = false;}复制代码

说明一下存储文件的创建(分为文件和共享内存)

MmapedFile::MmapedFile(const std::string &path, size_t size, bool fileType)    : m_name(path), m_fd(-1), m_segmentPtr(nullptr), m_segmentSize(0), m_fileType(fileType) {    if (m_fileType == MMAP_FILE) {  //文件创建        //创建一个文件        m_fd = open(m_name.c_str(), O_RDWR | O_CREAT, S_IRWXU);        if (m_fd < 0) {            MMKVError("fail to open:%s, %s", m_name.c_str(), strerror(errno));        } else {            //文件属性的访问            struct stat st = {};            if (fstat(m_fd, &st) != -1) {                m_segmentSize = static_cast(st.st_size);            }            if (m_segmentSize < DEFAULT_MMAP_SIZE) {                m_segmentSize = static_cast(DEFAULT_MMAP_SIZE);                if (ftruncate(m_fd, m_segmentSize) != 0 || !zeroFillFile(m_fd, 0, m_segmentSize)) {                    MMKVError("fail to truncate [%s] to size %zu, %s", m_name.c_str(),                              m_segmentSize, strerror(errno));                    close(m_fd);                    m_fd = -1;                    removeFile(m_name);                    return;                }            }            m_segmentPtr =                (char *) mmap(nullptr, m_segmentSize, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);            if (m_segmentPtr == MAP_FAILED) {                MMKVError("fail to mmap [%s], %s", m_name.c_str(), strerror(errno));                close(m_fd);                m_fd = -1;                m_segmentPtr = nullptr;            }        }    } else {        //共享内存创建 #define ASHMEM_NAME_DEF "/dev/ashmem"        m_fd = open(ASHMEM_NAME_DEF, O_RDWR);         if (m_fd < 0) { //创建失败            MMKVError("fail to open ashmem:%s, %s", m_name.c_str(), strerror(errno));        } else {            if (ioctl(m_fd, ASHMEM_SET_NAME, m_name.c_str()) != 0) {  //内存访问io控制                MMKVError("fail to set ashmem name:%s, %s", m_name.c_str(), strerror(errno));            } else if (ioctl(m_fd, ASHMEM_SET_SIZE, size) != 0) {                MMKVError("fail to set ashmem:%s, size %d, %s", m_name.c_str(), size,                          strerror(errno));            } else { //访问成功                 //获取页长度                m_segmentSize = static_cast(size);                //获取页指针                m_segmentPtr = (char *) mmap(nullptr, m_segmentSize, PROT_READ | PROT_WRITE,                                             MAP_SHARED, m_fd, 0);                if (m_segmentPtr == MAP_FAILED) {                    MMKVError("fail to mmap [%s], %s", m_name.c_str(), strerror(errno));                    m_segmentPtr = nullptr;                } else {                    return;                }            }            close(m_fd);            m_fd = -1;        }    }}复制代码

本节是第1篇,第2~4篇的分析,已经在小专栏里刊登,请浏览https://xiaozhuanlan.com/cangwang_process查看更详细的分析.

更多相关文章

  1. android 模拟器手机如何添加文件到sd卡?
  2. error: Error parsing XML: unbound prefix 与 error: Invalid s
  3. Android(安卓)ListView动画(逐行显示动画效果)
  4. Base64方式上传文件
  5. Android(安卓)studio的那些坑- so文件添加的正确位置
  6. Android(安卓)ADB详细介绍及用法
  7. Android应用程序四大组件之使用AIDL如何实现跨进程调用Service
  8. 从assets文件夹中读取文件
  9. Android(安卓)OpenCV_face使用OpenCV3.20的一个免安装OpenCV Man

随机推荐

  1. Android InputStream转Bitmap
  2. Android跑Monkey必备代码
  3. Android TimePickerDialog(原生安卓时间选
  4. Android 自定义AlertDialog
  5. android开发问题总结
  6. Android 中延迟执行的小结
  7. android动态申请拍照获取照片权限
  8. 徽章系列1: Top 30 android 开源项目徽章
  9. Android中用Toast.cancel()方法优化toast
  10. Android之-android-support-v4.jar的混淆