sharedpreferences 重启不保存_MMKV为什么可以替换SharedPreferences
MMKV介紹
MMKV——基于 mmap 的高性能通用 key-value 組件,底層序列化/反序列化使用 protobuf 實現(xiàn),性能高,穩(wěn)定性強。
https://github.com/Tencent/MMKV/blob/master/readme_cn.md
MMKV 是基于 mmap 內(nèi)存映射的移動端通用 key-value 組件,底層序列化/反序列化使用 protobuf 實現(xiàn),性能高,穩(wěn)定性強。
從 2015 年中至今,在 iOS 微信上使用已有近 3 年,其性能和穩(wěn)定性經(jīng)過了時間的驗證。
近期已移植到 Android 平臺。在騰訊內(nèi)部開源半年之后,得到公司內(nèi)部團隊的廣泛應(yīng)用和一致好評。
通過 mmap 內(nèi)存映射文件,提供一段可供隨時寫入的內(nèi)存塊,App 只管往里面寫數(shù)據(jù),
由操作系統(tǒng)負責將內(nèi)存回寫到文件,不必擔心 crash 導(dǎo)致數(shù)據(jù)丟失。
XML、JSON 更注重數(shù)據(jù)結(jié)構(gòu)化,關(guān)注人類可讀性和語義表達能力。
ProtoBuf 更注重數(shù)據(jù)序列化,關(guān)注效率、空間、速度,人類可讀性差,語義表達能力不足(為保證極致的效率,會舍棄一部分元信息)
MMKV特點
1.高性能
可以支持實時寫入
2.穩(wěn)定性非常好
3.多進程訪問
通過與 Android 開發(fā)同學的溝通,了解到系統(tǒng)自帶的 SharedPreferences 對多進程的支持不好。
現(xiàn)有基于 ContentProvider 封裝的實現(xiàn),雖然多進程是支持了,但是性能低下,經(jīng)常導(dǎo)致 ANR。
考慮到 mmap 共享內(nèi)存本質(zhì)上的多進程共享的,我們在這個基礎(chǔ)上,深入挖掘了 Android 系統(tǒng)的能力,提供了可能是業(yè)界最高效的多進程數(shù)據(jù)共享組件。
4.匿名內(nèi)存
在多進程共享的基礎(chǔ)上,考慮到某些敏感數(shù)據(jù)(例如密碼)需要進程間共享,但是不方便落地存儲到文件上,直接用 mmap 不合適。
我們了解到 Android 系統(tǒng)提供了 Ashmem 匿名共享內(nèi)存的能力,發(fā)現(xiàn)它在進程退出后就會消失,不會落地到文件上,非常適合這個場景。
我們很愉快地提供了 Ashmem MMKV 的功能。
5.數(shù)據(jù)加密
不像 iOS 提供了硬件層級的加密機制,在 Android 環(huán)境里,數(shù)據(jù)加密是非常必須的。
MMKV 使用了 AES CFB-128 算法來加密/解密。我們選擇 CFB 而不是常見的 CBC 算法,
主要是因為 MMKV 使用 append-only 實現(xiàn)插入/更新操作,流式加密算法更加合適。
6.數(shù)據(jù)有效性
考慮到文件系統(tǒng)、操作系統(tǒng)都有一定的不穩(wěn)定性,我們另外增加了 crc 校驗,對無效數(shù)據(jù)進行甄別。
MMKV 原理
1.內(nèi)存準備
通過 mmap 內(nèi)存映射文件,提供一段可供隨時寫入的內(nèi)存塊,App 只管往里面寫數(shù)據(jù),由操作系統(tǒng)負責將內(nèi)存回寫到文件,不必擔心 crash 導(dǎo)致數(shù)據(jù)丟失。
2.數(shù)據(jù)組織
數(shù)據(jù)序列化方面我們選用 protobuf 協(xié)議,pb 在性能和空間占用上都有不錯的表現(xiàn)。
3.寫入優(yōu)化
考慮到主要使用場景是頻繁地進行寫入更新,我們需要有增量更新的能力。我們考慮將增量 kv 對象序列化后,append 到內(nèi)存末尾。
這樣同一個 key 會有新舊若干份數(shù)據(jù),最新的數(shù)據(jù)在最后;那么只需在程序啟動第一次打開 mmkv 時,不斷用后讀入的 value 替換之前的值,就可以保證數(shù)據(jù)是最新有效的。
4.空間增長
使用 append 實現(xiàn)增量更新帶來了一個新的問題,就是不斷 append 的話,文件大小會增長得不可控。我們需要在性能和空間上做個折中。
以內(nèi)存 pagesize 為單位申請空間,在空間用盡之前都是 append 模式;當 append 到文件末尾時,進行文件重整、key 排重,嘗試序列化保存排重結(jié)果;
排重后空間還是不夠用的話,將文件擴大一倍,直到空間足夠。
5.數(shù)據(jù)有效性
考慮到文件系統(tǒng)、操作系統(tǒng)都有一定的不穩(wěn)定性,我們另外增加了 crc 校驗,對無效數(shù)據(jù)進行甄別。
詳細的原理請參考:https://github.com/Tencent/MMKV/wiki/design
MMKV集成
1.依賴注入
在 App 模塊的 build.gradle 文件里添加:
dependencies {implementation 'com.tencent:mmkv-static:1.2.2'
// replace "1.2.2" with any available version
}
Gradle 在編譯工程的時候會自動從 maven 倉庫下載 AAR 包。
MMKV 默認以靜態(tài)庫形式鏈接 libc++。這個庫如果動態(tài)鏈接,會額外占用 2MB 空間(解壓后)。如果你已經(jīng)有其他庫引入了 libc++_shared.so,并且你確保他們的庫沒有版本兼容問題,你可以使用動態(tài)鏈接 libc++ 的 MMKV,以進一步減少安裝包大小:
dependencies {implementation 'com.tencent:mmkv:1.2.2'
// replace "1.2.2" with any available version
}
2.初始化
// 設(shè)置初始化的根目錄String dir = getFilesDir().getAbsolutePath() + "/mmkv_2";
String rootDir = MMKV.initialize(dir);
Log.i("MMKV", "mmkv root: " + rootDir);
3.獲取實例
// 獲取默認的全局實例MMKV kv = MMKV.defaultMMKV();
// 根據(jù)業(yè)務(wù)區(qū)別存儲, 附帶一個自己的 ID
MMKV kv = MMKV.mmkvWithID("MyID");
// 多進程同步支持
MMKV kv = MMKV.mmkvWithID("MyID", MMKV.MULTI_PROCESS_MODE);
4.具體操作
// 添加/更新數(shù)據(jù)kv.encode(key, value);
// 獲取數(shù)據(jù)
int tmp = kv.decodeInt(key);
// 刪除數(shù)據(jù)
kv.removeValueForKey(key);
5.SharedPreferences遷移
private void testImportSharedPreferences() {MMKV mmkv = MMKV.mmkvWithID("myData");
SharedPreferences old_man = getSharedPreferences("myData", MODE_PRIVATE);
// 遷移舊數(shù)據(jù)
mmkv.importFromSharedPreferences(old_man);
// 清空舊數(shù)據(jù)
old_man.edit().clear().commit();
......
}
詳細的集成文檔請參考:https://github.com/Tencent/MMKV/wiki/android_setup_cn
性能測試
以下是 MMKV、SharedPreferences 和 SQLite 同步寫入 1000 條數(shù)據(jù)的測試結(jié)果
// MMKVMMKV write int: loop[1000]: 12 ms
MMKV read int: loop[1000]: 3 ms
MMKV write String: loop[1000]: 7 ms
MMKV read String: loop[1000]: 4 ms
// SharedPreferences
SharedPreferences write int: loop[1000]: 119 ms
SharedPreferences read int: loop[1000]: 3 ms
SharedPreferences write String: loop[1000]: 187
SharedPreferences read String: loop[1000]: 2 ms
// SQLite
sqlite write int: loop[1000]: 101 ms
sqlite read int: loop[1000]: 136 ms
sqlite write String: loop[1000]: 29 ms
sqlite read String: loop[1000]: 93 ms
可以看到 MMKV 無論是對比 SP 還是 SQLite, 在性能上都有非常大的優(yōu)勢, 官方提供的數(shù)據(jù)測試結(jié)果如下
1.單進程性能
可見,MMKV 在寫入性能上遠遠超越 SharedPreferences & SQLite,在讀取性能上也有相近或超越的表現(xiàn)。
2.多進程性能
可見,MMKV 無論是在寫入性能還是在讀取性能,都遠遠超越 MultiProcessSharedPreferences & SQLite & SQLite,
MMKV 在 Android 多進程 key-value 存儲組件上是不二之選。
核心原理
Linux的內(nèi)存分用戶空間跟內(nèi)核空間,同時頁表有也分兩類,用戶空間頁表跟內(nèi)核空間頁表,每個進程有一個用戶空間頁表,但是系統(tǒng)只有一個內(nèi)核空間頁表。
而Binder mmap的關(guān)鍵:更新用戶空間對應(yīng)的頁表的同時也同步映射內(nèi)核頁表,讓兩個頁表都指向同一塊地址。
這樣一來,數(shù)據(jù)只需要從A進程的用戶空間,直接拷貝到B所對應(yīng)的內(nèi)核空間,而B多對應(yīng)的內(nèi)核空間在B進程的用戶空間也有相應(yīng)的映射,這樣就無需從內(nèi)核拷貝到用戶空間了。
copy_from_user() //將數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間
copy_to_user() //將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間
mmap VS 普通文件IO
1.普通文件IO
通過read/write系統(tǒng)調(diào)訪問,先在用戶空間分配一段buffer,然后,進入內(nèi)核,將內(nèi)容從磁盤讀取到內(nèi)核緩沖,最后,拷貝到用戶進程空間,至少牽扯到兩次數(shù)據(jù)拷貝;
同時,多個進程同時訪問一個文件,每個進程都有一個副本,存在資源浪費的問題。
2.mmap
通過mmap來訪問文件,mmap()將文件直接映射到用戶空間,文件在mmap的時候,內(nèi)存并未真正分配,
只有在第一次讀取/寫入的時候才會觸發(fā),這個時候,會引發(fā)缺頁中斷,在處理缺頁中斷的時候,完成內(nèi)存也分配,同時也完成文件數(shù)據(jù)的拷貝。
并且,修改用戶空間對應(yīng)的頁表,完成到物理內(nèi)存到用戶空間的映射,這種方式只存在一次數(shù)據(jù)拷貝,效率更高。
同時多進程間通過mmap共享文件數(shù)據(jù)的時候,僅需要一塊物理內(nèi)存就夠了。
Android中使用mmap,可以通過RandomAccessFile與MappedByteBuffer來配合。
通過randomAccessFile.getChannel().map獲取到MappedByteBuffer。然后調(diào)用ByteBuffer的put方法添加數(shù)據(jù)。
MappedByteBuffer mappedByteBuffer= randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE,0, randomAccessFile.length());
mappedByteBuffer.putChar('c');
mappedByteBuffer.getChar();
總結(jié)
通過上面的分析, 我們對 MMKV 有了一個整體上的把控, 其具體的表現(xiàn)如下所示
| 正確性 | 優(yōu) | 支持多進程安全, 使用 mmap, 由操作系統(tǒng)保證數(shù)據(jù)回寫的正確性 |
| 時間開銷 | 優(yōu) | 使用 mmap 實現(xiàn), 減少了用戶空間數(shù)據(jù)到內(nèi)核空間的拷貝 |
| 空間開銷 | 中 | 使用 protocl buffer 存儲數(shù)據(jù), 同樣的數(shù)據(jù)會比 xml 和 json 消耗空間小,使用的是數(shù)據(jù)追加到末尾的方式, 只有到達一定閾值之后才會觸發(fā)鍵值合并, 不合并之前會導(dǎo)致同一個 key 存在多份 |
| 安全 | 中 | 使用 crc 校驗, 甄別文件系統(tǒng)和操作系統(tǒng)不穩(wěn)定導(dǎo)致的異常數(shù)據(jù) |
| 開發(fā)成本 | 優(yōu) | 使用方式較為簡單 |
| 兼容性 | 優(yōu) | 各個安卓版本都前后兼容 |
雖然 MMKV 一些場景下比 SP 稍慢(如: 首次實例化會進行數(shù)據(jù)的復(fù)寫剔除重復(fù)數(shù)據(jù), 比 SP 稍慢, 查詢數(shù)據(jù)時存在 ProtocolBuffer 解碼, 比 SP 稍慢), 但其逆天的數(shù)據(jù)寫入速度、mmap Linux 內(nèi)核保證數(shù)據(jù)的同步, 以及 ProtocolBuffer 編碼帶來的更小的本地存儲空間占用等都是非常棒的閃光點。
參考文獻:
https://www.jianshu.com/p/65334d245bc4
https://blog.csdn.net/gpf1320253667/article/details/91352887
https://github.com/Tencent/MMKV/releases
總結(jié)
以上是生活随笔為你收集整理的sharedpreferences 重启不保存_MMKV为什么可以替换SharedPreferences的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python torch exp_Pyt
- 下一篇: python生成器和迭代器作用_浅谈Py