面試官: IO優化是怎麼作的,使用 SharedPreferences爲何這麼卡,mmkv原理是什麼android
心理分析:IO優化一直是每一個企業必選項,每次聞到都很頭疼,面試官想問有沒有相關經驗,若是有的話,只有兩種答案sqlitedatabse, SharedPreferences。 這兩個很常見,確定不是面試官想問的。 那只有一種答案了,對,就是最新的mmkv框架git
接下來,會問你他的原理 你是怎麼看。 它的優缺點。爲何比其餘的好。從原理層來解析。這纔是最難的。github
這篇文章 從原理層說明他們的區別面試
更多面試內容,面試專題,flutter視頻 全套,音視頻從0到高手開發。
關注 GitHub: https://github.com/xiangjiana/Android-MS
免費獲取面試PDF合集
mkkv是基於 mmap 的高性能通用 key-value 組件,底層序列化/反序列化使用 protobuf 實現,性能高,穩定性強。算法
mmkv github下載地址
咱們將 MMKV 和 SharedPreferences、SQLite 進行對比, 重複讀寫操做 1k 次。相關測試代碼在 Android/MMKV/mmkvdemo/。結果以下圖表。sql
單進程性能
可見,MMKV 在寫入性能上遠遠超越 SharedPreferences & SQLite,在讀取性能上也有相近或超越的表現。
可見,MMKV 不管是在寫入性能仍是在讀取性能,都遠遠超越 MultiProcessSharedPreferences & SQLite & SQLite, MMKV 在 Android 多進程 key-value 存儲組件上是不二之選。緩存
更詳細的設計原理參考 MMKV 原理。微信
dependencies { implementation 'com.tencent:mmkv:1.0.23' // replace "1.0.23" with any available version }
MMKV)的使用很是簡單, 全部變動立馬生效,無需調用 sync、apply。 在 App 啓動時初始化 MMKV,設定 MMKV 的根目錄 (默認/data/data/xxx.xxx/files/mmkv/) (sp存儲在/data/data/xxx.xxx/shared_prefs/)數據結構
支持從SP遷移數據importFromSharedPreferencesapp
MMKV 還額外實現了一遍 SharedPreferences、SharedPreferences.Editor 這兩個 interface
// 能夠跟SP用法同樣 SharedPreferences.Editor editor = mmkv.edit(); // 無需調用 commit() //editor.commit();
MMKV 的使用很是簡單,全部變動立馬生效,無需調用 sync、apply。 在 App 啓動時初始化 MMKV,設定 MMKV 的根目錄(files/mmkv/),例如在 MainActivity 裏:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String rootDir = MMKV.initialize(this); System.out.println("mmkv root: " + rootDir); //…… }
MMKV 提供一個全局的實例,能夠直接使用:
import com.tencent.mmkv.MMKV; //…… MMKV kv = MMKV.defaultMMKV(); kv.encode("bool", true); boolean bValue = kv.decodeBool("bool"); kv.encode("int", Integer.MIN_VALUE); int iValue = kv.decodeInt("int"); kv.encode("string", "Hello from mmkv"); String str = kv.decodeString("string");
使用完畢的幾個方法
public native void clearAll(); // MMKV's size won't reduce after deleting key-values // call this method after lots of deleting f you care about disk usage // note that `clearAll` has the similar effect of `trim` public native void trim(); // call this method if the instance is no longer needed in the near future // any subsequent call to the instance is undefined behavior public native void close(); // call on memory warning // any subsequent call to the instance will load all key-values from file again public native void clearMemoryCache(); // you don't need to call this, really, I mean it // unless you care about out of battery public void sync() { sync(true); }
若是使用請務必作code19版本的適配,這個在github官網有說明
依賴下面這個庫,而後對19區分處理
implementation ‘com.getkeepsafe.relinker:relinker:1.3.1’
if (android.os.Build.VERSION.SDK_INT == 19) { MMKV.initialize(relativePath, new MMKV.LibLoader() { @Override public void loadLibrary(String libName) { ReLinker.loadLibrary(context, libName); } }); } else { MMKV.initialize(context); }
可看到,一個鍵會存入多分實例,最後存入的就是最新的。
MMKV 在大部分狀況下都性能強勁,key/value 的數量和長度都沒有限制。
然而 MMKV 在內存裏緩存了全部的 key-value,在總大小比較大的狀況下(例如 100M+),App 可能會爆內存,觸發重整回寫時,寫入速度也會變慢。
支持大文件的 MMKV 正在開發中,有望在下一個大版本發佈。
注意若是一個進程lock住,另外一個進程mmkvWithID獲取MMKV時就阻塞住,直到持有進程釋放。
// get the lock immediately MMKV mmkv2 = MMKV.mmkvWithID(LOCK_PHASE_2, MMKV.MULTI_PROCESS_MODE); mmkv2.lock(); Log.d("locked in child", LOCK_PHASE_2); Runnable waiter = new Runnable() { @Override public void run() { //阻塞住 直到其餘進程釋放 MMKV mmkv1 = MMKV.mmkvWithID(LOCK_PHASE_1, MMKV.MULTI_PROCESS_MODE); mmkv1.lock(); Log.d("locked in child", LOCK_PHASE_1); } };
注意:若是其餘進程有進行修改,不會當即觸發onContentChangedByOuterProcess,
checkLoadData若是變化,會clearMemoryState,從新loadFromFile。//數據量大時不要太頻繁
讀取decodeXXX會阻塞住,先回調onContentChangedByOuterProcess,再返回值,保證值是最新的。
能夠進行進程間通訊,可設置pageSize
// a memory only MMKV, cleared on program exit
// size cannot change afterward (because ashmem won't allow it)
write速度: mmkv > cryptKV >> sp
read速度: sp > cryptKV > mmkv
Linux的內存分用戶空間跟內核空間,同時頁表有也分兩類,用戶空間頁表跟內核空間頁表,每一個進程有一個用戶空間頁表,可是系統只有一個內核空間頁表。
而Binder mmap的關鍵是:更新用戶空間對應的頁表的同時也同步映射內核頁表,讓兩個頁表都指向同一塊地址,
這樣一來,數據只須要從A進程的用戶空間,直接拷貝到B所對應的內核空間,而B多對應的內核空間在B進程的用戶空間也有相應的映射,這樣就無需從內核拷貝到用戶空間了。
copy_from_user() //將數據從用戶空間拷貝到內核空間
copy_to_user() //將數據從內核空間拷貝到用戶空間
1.8.1 Liunx進程隔離
1.8.2 傳統IPC
1.8.3 Binder通訊
普通文件的訪問方式有兩種:
只有在第一次讀取/寫入的時候纔會觸發,這個時候,會引起缺頁中斷,在處理缺頁中斷的時候,完成內存也分配,同時也完成文件數據的拷貝。
而且,修改用戶空間對應的頁表,完成到物理內存到用戶空間的映射,這種方式只存在一次數據拷貝,效率更高。
同時多進程間經過mmap共享文件數據的時候,僅須要一塊物理內存就夠了。
Android中使用mmap,能夠經過RandomAccessFile與MappedByteBuffer來配合。
經過randomAccessFile.getChannel().map獲取到MappedByteBuffer。而後調用ByteBuffer的put方法添加數據。
RandomAccessFile randomAccessFile = new RandomAccessFile("path","rw"); MappedByteBuffer mappedByteBuffer= randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE,0, mappedByteBuffer.putChar('c'); mappedByteBuffer.getChar();
更多面試內容,面試專題,flutter視頻 全套,音視頻從0到高手開發。
關注 GitHub: https://github.com/xiangjiana/Android-MS
免費獲取面試PDF合集