Android

【学习】MMKV:微信团队开源的轻量级存储方案

2021-05-14  本文已影响0人  Merbng

MMKV 轻量级存储方案

定义

优点

出现的意义

MMKV的出现是为了替代SharedPreferences的轻量级存储解决方案。SharedPreferences需要被替换的原因主要存在以下问题:

//1.同步提交commit,commit提交是同步的,直到磁盘操作成功后才会完成
//所以当数据量比较大时,使用commit很容易引起ANR
public boolean commit(){
    MemoryCommitResult mcr =commitToMemory();
    SharedPreferencesImpl.this.enqueueDiskWrite(mcr,null);
    try{
        mcr.writtenToDiaskLatch.await();
    }catch(InterruptedException e){
        reutrn false;
    }
}
//2.异步提交apply
//当数据量比较大时,使用apply也可能引起ANR
public void apply(){
    final long startTime =System.currentTimeMillis();
    
    final MemoryCommitResult mcr =commitToMemory();
    final Runable awaitCommit =new Runable(){
        @Override
        public void run(){
            //启用等待
            mcr.writtenToDiaskLatch.await();
            ...
        }
    };
    //将awaitCommit 添加到队列QueueWork中
    QueueWork.addFinisher(awaitCommit);
    
    Runable postWriteRunable = new Runable(){
        @Override
        public void run(){
            awaitCommit.run();
            QueueWork.removeFinisher(awaitCommit);
        }
    };
    //将写入任务加入到队列中,而写入任务在一个线程中执行
    SharedPreferencesImpl.this.enqueueDiskWrite(mcr,postWriteRunable);
    
    //为了保证异步任务及时完成,当生命周期处于handleStopService()、handlePauseActivity()、handleStopActivity()时会调用QueueWork.waitToFinish() 会等待写入任务执行完毕
    //waitToFinish():会一直等待写入任务执行完毕,其他什么都不做
    //当有很多写入任务,会依次执行;当文件很大时,效率很低,则容易造成ANR
    public static void waitToFinish(){
        Runable toFinish;
        while((toFinish=sPendingWorkFinishers.poll())!=null){
            toFinish.run();
        }
    }


//3. 获取数据getXXX()
//所有getXXX()方法都是同步的,在主线程调用get方法,必须等待SP加载完毕,也有可能导致ANR
//使用getSharedPreferences()最终会调用SharedPreferencesImpl#startLoadFormDish()开启一个线程异步读取数据
new Thread("SharedPreferencesImpl-load"){
    @Override
    public void run(){
        loadFromDisk();
    }
}.start();

//当我们正在读取一个比较大的数据,还没读取完,接着调用getXXX().
public String getString(String key,@Nullable String defValue){
    synchronized(mLock){
        awaitLoadedLocked();
        String v =(String)mMap.get(key);
        return v !=null ? v :defValue;
    }
}
//在同步方法内调用了wait(),会一直等待getSharedPreferences()开启线程读取完数据才能继续往下执行
//如果读取一个大的文件,也很大可能会造成ANR
    private void awaitLoadedLocked(){
    while(!mLoaded){
        mLock.wait();
    }catch(InterruptedException unused){}
    
    }
}

MMKV原理

  1. 读写方式:内存映射MMAP
    MMKV基于内存映射MMAP,下面主要介绍内存映射相关的内容:

1.1 定义

Linux通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存的内容,这个过程称为内存映射(memory mapping)


image.png

1.2 读写原理

1.3 优势

2.数据存储方式:Protobuf
MMKV的序列化/反序列化使用protobuf实现,其采用了以T-V方式对数据进行二进制数据流存储,空间占存少、数据量精简,能以最少的数据量能表示最多的信息。


image.png
  1. 写入方式
    因为序列化/反序列化使用protobuf实现,在更新数据的时候,只需将数据追加在前数据后,效率更高,可实现增量更新
    本文来自Carson_Ho,其公众号:Carson带你学习Android
    查看原文
上一篇 下一篇

猜你喜欢

热点阅读