mmkv源碼解析(java)

一、成員變量

//錯誤恢復(fù)策略相關(guān)

private static final EnumMap<MMKVRecoverStrategic, Integer> recoverIndex = new EnumMap(MMKVRecoverStrategic.class);

//MMKV日志輸出相關(guān)

private static final EnumMap<MMKVLogLevel, Integer> logLevel2Index;

private static final MMKVLogLevel[] index2LogLevel;

//存儲已經(jīng)打開的 MMKV 描述符集合

private static final Set<Long> checkedHandleSet;

//MMKV 文件存放根路徑

private static String rootDir;

//MMKV 進(jìn)程模式

public static final int SINGLE_PROCESS_MODE = 1;

public static final int MULTI_PROCESS_MODE = 2;

private static final int CONTEXT_MODE_MULTI_PROCESS = 4;

private static final int ASHMEM_MODE = 8;

//序列化相關(guān)的

private static final HashMap<String, Creator<?>> mCreators;

//處理日志重定向询枚、文件錯誤恢復(fù)策略

private static MMKVHandler gCallbackHandler;

private static boolean gWantLogReDirecting;

//內(nèi)容變更通知 主要是被其他進(jìn)程更新時有回調(diào)

private static MMKVContentChangeNotification gContentChangeNotify;

//MMKV描述符 主要用來讀寫文件用

private final long nativeHandle;

二螟炫、API接口

1.初始化MMKV

//傳入context的初始化函數(shù)折欠,log默認(rèn)是LevelInfo

public static String initialize(Context context) {

? ? String root = context.getFilesDir().getAbsolutePath() + "/mmkv";

? ? MMKVLogLevel logLevel = MMKVLogLevel.LevelInfo;

? ? return initialize((String)root, (MMKV.LibLoader)null, logLevel);

}

//傳入context 和 logLevel

public static String initialize(Context context, MMKVLogLevel logLevel) {

? ? String root = context.getFilesDir().getAbsolutePath() + "/mmkv";

? ? return initialize((String)root, (MMKV.LibLoader)null, logLevel);

}

//傳入context 和 loader

public static String initialize(Context context, MMKV.LibLoader loader) {

? ? String root = context.getFilesDir().getAbsolutePath() + "/mmkv";

? ? MMKVLogLevel logLevel = MMKVLogLevel.LevelInfo;

? ? return initialize(root, loader, logLevel);

}

// 傳入 context 固阁、loader/level

public static String initialize(Context context, MMKV.LibLoader loader, MMKVLogLevel logLevel) {

? ? String root = context.getFilesDir().getAbsolutePath() + "/mmkv";

? ? return initialize(root, loader, logLevel);

}

// 傳入路徑

public static String initialize(String rootDir) {

? ? MMKVLogLevel logLevel = MMKVLogLevel.LevelInfo;

? ? return initialize((String)rootDir, (MMKV.LibLoader)null, logLevel);

}

//傳入路徑和日志等級

public static String initialize(String rootDir, MMKVLogLevel logLevel) {

? ? return initialize((String)rootDir, (MMKV.LibLoader)null, logLevel);

}

//傳入路徑和loader

public static String initialize(String rootDir, MMKV.LibLoader loader) {

? ? MMKVLogLevel logLevel = MMKVLogLevel.LevelInfo;

? ? return initialize(rootDir, loader, logLevel);

}

//最后都調(diào)用的該方法? 傳入路徑 /loader 和 日志等級

public static String initialize(String rootDir, MMKV.LibLoader loader, MMKVLogLevel logLevel) {

? ? //如果loader不為空夭禽,則由傳入的loader 加載 c++_shared 和 mmkv 兩個so庫或颊,否則由System.loadLibrary 加載

? ? if (loader != null) {

? ? ? ? if ("SharedCpp".equals("SharedCpp")) {

? ? ? ? ? ? loader.loadLibrary("c++_shared");

? ? ? ? }

? ? ? ? loader.loadLibrary("mmkv");

? ? } else {

? ? ? ? if ("SharedCpp".equals("SharedCpp")) {

? ? ? ? ? ? System.loadLibrary("c++_shared");

? ? ? ? }

? ? ? ? System.loadLibrary("mmkv");

? ? }

? ? //jni 調(diào)用native方法

? ? jniInitialize(rootDir, logLevel2Int(logLevel));

? ? //記錄rootDir

? ? MMKV.rootDir = rootDir;

? ? return MMKV.rootDir;

}

總結(jié):初始化最后都調(diào)用的是 initialize(String rootDir, MMKV.LibLoader loader, MMKVLogLevel logLevel)

2.獲取MMKV存儲根路徑

//可以根據(jù)此方法判斷路徑是否為空 是否初始化成功砸紊,但是要注意是否在調(diào)用exit 后

public static String getRootDir() {

? ? return rootDir;

}

3.獲取MMKV

//通過mapId 獲取,默認(rèn)單進(jìn)程 獲取前必須保證初始化 即路徑不為空

@Nullable

public static MMKV mmkvWithID(String mmapID) {

? ? if (rootDir == null) {

? ? ? ? throw new IllegalStateException("You should Call MMKV.initialize() first.");

? ? } else {

? ? ? ? long handle = getMMKVWithID(mmapID, 1, (String)null, (String)null);

? ? ? ? return checkProcessMode(handle, mmapID, 1);

? ? }

}

//通過mapId 、進(jìn)程mode 獲取

@Nullable

public static MMKV mmkvWithID(String mmapID, int mode) {

? ? if (rootDir == null) {

? ? ? ? throw new IllegalStateException("You should Call MMKV.initialize() first.");

? ? } else {

? ? ? ? long handle = getMMKVWithID(mmapID, mode, (String)null, (String)null);

? ? ? ? return checkProcessMode(handle, mmapID, mode);

? ? }

}

//通過mapId mode 饭宾、key 獲取

@Nullable

public static MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey) {

? ? if (rootDir == null) {

? ? ? ? throw new IllegalStateException("You should Call MMKV.initialize() first.");

? ? } else {

? ? ? ? long handle = getMMKVWithID(mmapID, mode, cryptKey, (String)null);

? ? ? ? return checkProcessMode(handle, mmapID, mode);

? ? }

}

//通過mapID 和 rootpath 獲取

@Nullable

public static MMKV mmkvWithID(String mmapID, String rootPath) {

? ? if (rootDir == null) {

? ? ? ? throw new IllegalStateException("You should Call MMKV.initialize() first.");

? ? } else {

? ? ? ? long handle = getMMKVWithID(mmapID, 1, (String)null, rootPath);

? ? ? ? return checkProcessMode(handle, mmapID, 1);

? ? }

}

//通過 mapId? rootpath? 進(jìn)程批糟、key 獲取

@Nullable

public static MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey, String rootPath) {

? ? if (rootDir == null) {

? ? ? ? throw new IllegalStateException("You should Call MMKV.initialize() first.");

? ? } else {

? ? ? ? long handle = getMMKVWithID(mmapID, mode, cryptKey, rootPath);

? ? ? ? return checkProcessMode(handle, mmapID, mode);

? ? }

}

//匿名共享內(nèi)存方式獲取? 跨進(jìn)程傳輸 MMKV對應(yīng)的 handle /size/ mode/key

@Nullable

public static MMKV mmkvWithAshmemID(Context context, String mmapID, int size, int mode, @Nullable String cryptKey) {

? ? if (rootDir == null) {

? ? ? ? throw new IllegalStateException("You should Call MMKV.initialize() first.");

? ? } else {

? ? ? ? String processName = MMKVContentProvider.getProcessNameByPID(context, Process.myPid());

? ? ? ? if (processName != null && processName.length() != 0) {

? ? ? ? ? ? if (processName.contains(":")) {

? ? ? ? ? ? ? ? Uri uri = MMKVContentProvider.contentUri(context);

? ? ? ? ? ? ? ? if (uri == null) {

? ? ? ? ? ? ? ? ? ? simpleLog(MMKVLogLevel.LevelError, "MMKVContentProvider has invalid authority");

? ? ? ? ? ? ? ? ? ? return null;

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? simpleLog(MMKVLogLevel.LevelInfo, "getting parcelable mmkv in process, Uri = " + uri);

? ? ? ? ? ? ? ? ? ? Bundle extras = new Bundle();

? ? ? ? ? ? ? ? ? ? extras.putInt("KEY_SIZE", size);

? ? ? ? ? ? ? ? ? ? extras.putInt("KEY_MODE", mode);

? ? ? ? ? ? ? ? ? ? if (cryptKey != null) {

? ? ? ? ? ? ? ? ? ? ? ? extras.putString("KEY_CRYPT", cryptKey);

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? ContentResolver resolver = context.getContentResolver();

? ? ? ? ? ? ? ? ? ? Bundle result = resolver.call(uri, "mmkvFromAshmemID", mmapID, extras);

? ? ? ? ? ? ? ? ? ? if (result != null) {

? ? ? ? ? ? ? ? ? ? ? ? result.setClassLoader(ParcelableMMKV.class.getClassLoader());

? ? ? ? ? ? ? ? ? ? ? ? ParcelableMMKV parcelableMMKV = (ParcelableMMKV)result.getParcelable("KEY");

? ? ? ? ? ? ? ? ? ? ? ? if (parcelableMMKV != null) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? MMKV mmkv = parcelableMMKV.toMMKV();

? ? ? ? ? ? ? ? ? ? ? ? ? ? if (mmkv != null) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? simpleLog(MMKVLogLevel.LevelInfo, mmkv.mmapID() + " fd = " + mmkv.ashmemFD() + ", meta fd = " + mmkv.ashmemMetaFD());

? ? ? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? ? ? ? ? return mmkv;

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? return null;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? simpleLog(MMKVLogLevel.LevelInfo, "getting mmkv in main process");

? ? ? ? ? ? ? ? mode |= 8;

? ? ? ? ? ? ? ? long handle = getMMKVWithIDAndSize(mmapID, size, mode, cryptKey);

? ? ? ? ? ? ? ? return new MMKV(handle);

? ? ? ? ? ? }

? ? ? ? } else {

? ? ? ? ? ? simpleLog(MMKVLogLevel.LevelError, "process name detect fail, try again later");

? ? ? ? ? ? return null;

? ? ? ? }

? ? }

}

//獲取默認(rèn)的存儲MMKV

@Nullable

public static MMKV defaultMMKV() {

? ? if (rootDir == null) {

? ? ? ? throw new IllegalStateException("You should Call MMKV.initialize() first.");

? ? } else {

? ? ? ? //默認(rèn)獲取的mode是單進(jìn)程格了,不加密看铆,默認(rèn)獲取的mapId 是DefaultMMKV

? ? ? ? long handle = getDefaultMMKV(1, (String)null);

? ? ? ? return checkProcessMode(handle, "DefaultMMKV", 1);

? ? }

}

//獲取默認(rèn)的存儲文件

@Nullable

public static MMKV defaultMMKV(int mode, @Nullable String cryptKey) {

? ? if (rootDir == null) {

? ? ? ? throw new IllegalStateException("You should Call MMKV.initialize() first.");

? ? } else {

? ? ? ? long handle = getDefaultMMKV(mode, cryptKey);

? ? ? ? return checkProcessMode(handle, "DefaultMMKV", mode);

? ? }

}

//檢查進(jìn)程模式? handle類似句柄不能為0,校驗(yàn)進(jìn)程模式盛末,如果模式不匹配弹惦,會拋異常

//handle 會存入本地的set,如果本地?zé)o記錄handle, 則將當(dāng)前模式和handle進(jìn)行校驗(yàn)悄但,不匹配則報(bào)錯棠隐,拋出異常

//模式校驗(yàn)通過,則將handle 賦值給變量值 private final long nativeHandle檐嚣,即new MMKV();

//這里的疑問見后續(xù)章節(jié)分析助泽,發(fā)現(xiàn)如果用不同進(jìn)程模式取獲取同一文件名,獲取到的handle值是一樣的嚎京,并未報(bào)錯

@Nullable

private static MMKV checkProcessMode(long handle, String mmapID, int mode) {

? ? if (handle == 0L) {

? ? ? ? return null;

? ? } else {

? ? ? ? if (!checkedHandleSet.contains(handle)) {

? ? ? ? ? ? if (!checkProcessMode(handle)) {

? ? ? ? ? ? ? ? String message;

? ? ? ? ? ? ? ? if (mode == 1) {

? ? ? ? ? ? ? ? ? ? message = "Opening a multi-process MMKV instance [" + mmapID + "] with SINGLE_PROCESS_MODE!";

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? message = "Opening a MMKV instance [" + mmapID + "] with MULTI_PROCESS_MODE, ";

? ? ? ? ? ? ? ? ? ? message = message + "while it's already been opened with SINGLE_PROCESS_MODE by someone somewhere else!";

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? throw new IllegalArgumentException(message);

? ? ? ? ? ? }

? ? ? ? ? ? checkedHandleSet.add(handle);

? ? ? ? }

? ? ? ? return new MMKV(handle);

? ? }

}

4.功能接口

/****退出功能******/

//退出MMKV嗡贺,針對的是全局MMKV,退出后必須重新調(diào)用initialize 否則再獲取MMKV 實(shí)例時會拋異常

public static native void onExit();

//當(dāng)某個MMKV實(shí)例不再使用時 可以考慮調(diào)用此方法關(guān)掉,后續(xù)需要重新獲取該實(shí)例

public native void close();

/***操作 加解密 相關(guān)功能***/

//獲取秘鑰針對的是單個MMKV實(shí)例

@Nullable

public native String cryptKey();

//重置秘鑰鞍帝,針對單個MMKV文件诫睬,返回重置成功或者失敗

public native boolean reKey(@Nullable String var1);

public native void checkReSetCryptKey(@Nullable String var1);

/*****獲取size相關(guān)功能****/

//靜態(tài)方法 獲取pagesize? 返回默認(rèn)值 4K

public static native int pageSize();

//獲取某個key 對應(yīng)的value的size? 需要結(jié)合PB編碼 比如整型 1 PB編碼后獲取到的是 1,500獲取到的是 2帕涌,對于引用類型比如字符串摄凡,由于PB編碼會插入字節(jié)表示長度,這里獲取的大小是包含長度字節(jié)的蚓曼,比如“12345”亲澡,這里獲取到是6

public int getValueSize(String key) {

? ? return this.valueSize(this.nativeHandle, key, false);

}

//獲取某個key 對應(yīng)的value的實(shí)際size 需要結(jié)合PB編碼 大部分情況下獲取到的值和上述 getValueSize值是一樣的,但對于字符串類型 比如“12345”獲取到的是實(shí)際大小 5纫版,會去掉表示長度的字節(jié)

public int getValueActualSize(String key) {

? ? return this.valueSize(this.nativeHandle, key, true);

}

//該存儲所占用大小 返回的是 pagesize 的倍數(shù)

public long totalSize() {

? ? return this.totalSize(this.nativeHandle);

}

//測試結(jié)果是 Key的數(shù)量(不含重復(fù)項(xiàng));removeValueForkey 也會刪除key谷扣,clearALL 后變?yōu)?

public long count() {

? ? return this.count(this.nativeHandle);

}

/***操作key相關(guān)功能***/

//是否包含某個key

public boolean containsKey(String key) {

? ? return this.containsKey(this.nativeHandle, key);

}

//刪除某個key 對應(yīng)的value,同時key 也會刪除

public void removeValueForKey(String key) {

? ? this.removeValueForKey(this.nativeHandle, key);

}

@Nullable

//獲取所有key

public native String[] allKeys();

//刪除相關(guān)key對應(yīng)的value

public native void removeValuesForKeys(String[] var1);

/***清除功能***/

//清除所有數(shù)據(jù) 針對單個MMKV

public native void clearAll();

//觸發(fā)對齊 當(dāng)刪除很多key_value后強(qiáng)制對齊下?

public native void trim();

//清除緩存 由于和SP一樣的機(jī)制,會讀緩存加載所有keyvalue,內(nèi)存告警時可以清除,再次使用時會加載所有

public native void clearMemoryCache();

//獲取版本號

public static native String version();

//獲取文件名即mmapId

public native String mmapID();

/****鎖相關(guān) 多進(jìn)程時使用*****/

//加鎖

public native void lock();

//解鎖

public native void unlock();

//嘗試加鎖

public native boolean tryLock();

//文件是否有效

public static native boolean isFileValid(String var0);

/**強(qiáng)制觸發(fā)一次同步 寫文件**/

//一般情況下不要調(diào)用 除非你擔(dān)心 電池沒電了?

public void sync() {

? this.sync(true);

}

//

public void async() {

? this.sync(false);

}

//

private native void sync(boolean var1);

/***共享內(nèi)存相關(guān)***/

public native int ashmemFD();

public native int ashmemMetaFD();

/**當(dāng)從 MMKV 取一個 String or byte[]的時候会涎,會有一次從 native 到 JVM 的內(nèi)存拷貝裹匙。如果這個值立即傳遞到另*一個 native 庫(JNI),又會有一次從 JVM 到 native 的內(nèi)存拷貝末秃。當(dāng)這個值比較大的時候概页,整個過程會非常浪費(fèi)。*Native Buffer 就是為了解決這個問題练慕。

*Native Buffer 是由 native 創(chuàng)建的內(nèi)存緩沖區(qū)惰匙,在 Java 里封裝成 NativeBuffer 類型,可以透明傳遞到另一個 *native 庫進(jìn)行訪問處理铃将。整個過程避免了先拷內(nèi)存到 JVM 又從 JVM 拷回來導(dǎo)致的浪費(fèi)

**/

public static NativeBuffer createNativeBuffer(int size) {

? ? long pointer = createNB(size);

? ? return pointer <= 0L ? null : new NativeBuffer(pointer, size);

}

public static void destroyNativeBuffer(NativeBuffer buffer) {

? ? destroyNB(buffer.pointer, buffer.size);

}

public int writeValueToNativeBuffer(String key, NativeBuffer buffer) {

? ? return this.writeValueToNB(this.nativeHandle, key, buffer.pointer, buffer.size);

}

//多進(jìn)程通知監(jiān)聽

private static native void setWantsContentChangeNotify(boolean var0);

public native void checkContentChangedByOuterProcess();

5.寫/讀數(shù)據(jù)

//讀寫bool類型相關(guān)

public boolean encode(String key, boolean value) {

? ? return this.encodeBool(this.nativeHandle, key, value);

}

public boolean decodeBool(String key) {

? ? return this.decodeBool(this.nativeHandle, key, false);

}

public boolean decodeBool(String key, boolean defaultValue) {

? ? return this.decodeBool(this.nativeHandle, key, defaultValue);

}

//讀寫int 類型

public boolean encode(String key, int value) {

? ? return this.encodeInt(this.nativeHandle, key, value);

}

public int decodeInt(String key) {

? ? return this.decodeInt(this.nativeHandle, key, 0);

}

public int decodeInt(String key, int defaultValue) {

? ? return this.decodeInt(this.nativeHandle, key, defaultValue);

}

//讀寫long 類型

public boolean encode(String key, long value) {

? ? return this.encodeLong(this.nativeHandle, key, value);

}

public long decodeLong(String key) {

? ? return this.decodeLong(this.nativeHandle, key, 0L);

}

public long decodeLong(String key, long defaultValue) {

? ? return this.decodeLong(this.nativeHandle, key, defaultValue);

}

//讀寫float類型

public boolean encode(String key, float value) {

? ? return this.encodeFloat(this.nativeHandle, key, value);

}

public float decodeFloat(String key) {

? ? return this.decodeFloat(this.nativeHandle, key, 0.0F);

}

public float decodeFloat(String key, float defaultValue) {

? ? return this.decodeFloat(this.nativeHandle, key, defaultValue);

}

//讀寫double 類型

public boolean encode(String key, double value) {

? ? return this.encodeDouble(this.nativeHandle, key, value);

}

public double decodeDouble(String key) {

? ? return this.decodeDouble(this.nativeHandle, key, 0.0D);

}

public double decodeDouble(String key, double defaultValue) {

? ? return this.decodeDouble(this.nativeHandle, key, defaultValue);

}

//讀寫字符串類型

public boolean encode(String key, @Nullable String value) {

? ? return this.encodeString(this.nativeHandle, key, value);

}

@Nullable

public String decodeString(String key) {

? ? return this.decodeString(this.nativeHandle, key, (String)null);

}

@Nullable

public String decodeString(String key, @Nullable String defaultValue) {

? ? return this.decodeString(this.nativeHandle, key, defaultValue);

}

//讀寫 set<string> 類型

public boolean encode(String key, @Nullable Set<String> value) {

? ? return this.encodeSet(this.nativeHandle, key, value == null ? null : (String[])value.toArray(new String[0]));

}

@Nullable

public Set<String> decodeStringSet(String key) {

? ? return this.decodeStringSet(key, (Set)null);

}

@Nullable

public Set<String> decodeStringSet(String key, @Nullable Set<String> defaultValue) {

? ? return this.decodeStringSet(key, defaultValue, HashSet.class);

}

@Nullable

public Set<String> decodeStringSet(String key, @Nullable Set<String> defaultValue, Class<? extends Set> cls) {

? ? String[] result = this.decodeStringSet(this.nativeHandle, key);

? ? if (result == null) {

? ? ? ? return defaultValue;

? ? } else {

? ? ? ? Set a;

? ? ? ? try {

? ? ? ? ? ? a = (Set)cls.newInstance();

? ? ? ? } catch (IllegalAccessException var7) {

? ? ? ? ? ? return defaultValue;

? ? ? ? } catch (InstantiationException var8) {

? ? ? ? ? ? return defaultValue;

? ? ? ? }

? ? ? ? a.addAll(Arrays.asList(result));

? ? ? ? return a;

? ? }

}

//讀寫 byte[]

public boolean encode(String key, @Nullable byte[] value) {

? ? return this.encodeBytes(this.nativeHandle, key, value);

}

@Nullable

public byte[] decodeBytes(String key) {

? ? return this.decodeBytes(key, (byte[])null);

}

@Nullable

public byte[] decodeBytes(String key, @Nullable byte[] defaultValue) {

? ? byte[] ret = this.decodeBytes(this.nativeHandle, key);

? ? return ret != null ? ret : defaultValue;

}

//讀寫序列化類型 Parcelable

public boolean encode(String key, @Nullable Parcelable value) {

? ? if (value == null) {

? ? ? ? return this.encodeBytes(this.nativeHandle, key, (byte[])null);

? ? } else {

? ? ? ? Parcel source = Parcel.obtain();

? ? ? ? value.writeToParcel(source, value.describeContents());

? ? ? ? byte[] bytes = source.marshall();

? ? ? ? source.recycle();

? ? ? ? return this.encodeBytes(this.nativeHandle, key, bytes);

? ? }

}

@Nullable

public <T extends Parcelable> T decodeParcelable(String key, Class<T> tClass) {

? ? return this.decodeParcelable(key, tClass, (Parcelable)null);

}

@Nullable

public <T extends Parcelable> T decodeParcelable(String key, Class<T> tClass, @Nullable T defaultValue) {

? ? if (tClass == null) {

? ? ? ? return defaultValue;

? ? } else {

? ? ? ? byte[] bytes = this.decodeBytes(this.nativeHandle, key);

? ? ? ? if (bytes == null) {

? ? ? ? ? ? return defaultValue;

? ? ? ? } else {

? ? ? ? ? ? Parcel source = Parcel.obtain();

? ? ? ? ? ? source.unmarshall(bytes, 0, bytes.length);

? ? ? ? ? ? source.setDataPosition(0);

? ? ? ? ? ? Parcelable var8;

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? String name = tClass.toString();

? ? ? ? ? ? ? ? Creator creator;

? ? ? ? ? ? ? ? synchronized(mCreators) {

? ? ? ? ? ? ? ? ? ? //先從本地緩存中獲取

? ? ? ? ? ? ? ? ? ? creator = (Creator)mCreators.get(name);

? ? ? ? ? ? ? ? ? ? if (creator == null) {

? ? ? ? ? ? ? ? ? ? ? ? //獲取public 變量? CREATOR项鬼,? 由于是反射所以會保存到hashmap中 避免耗時

? ? ? ? ? ? ? ? ? ? ? ? Field f = tClass.getField("CREATOR");

? ? ? ? ? ? ? ? ? ? ? ? creator = (Creator)f.get((Object)null);

? ? ? ? ? ? ? ? ? ? ? ? if (creator != null) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? mCreators.put(name, creator);

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? //如果為空 則表明類沒有實(shí)現(xiàn)Parcelable 序列化

? ? ? ? ? ? ? ? if (creator == null) {

? ? ? ? ? ? ? ? ? ? throw new Exception("Parcelable protocol requires a non-null static Parcelable.Creator object called CREATOR on class " + name);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? var8 = (Parcelable)creator.createFromParcel(source);

? ? ? ? ? ? } catch (Exception var16) {

? ? ? ? ? ? ? ? simpleLog(MMKVLogLevel.LevelError, var16.toString());

? ? ? ? ? ? ? ? return defaultValue;

? ? ? ? ? ? } finally {

? ? ? ? ? ? ? ? source.recycle();

? ? ? ? ? ? }

? ? ? ? ? ? return var8;

? ? ? ? }

? ? }

}

//類型:bool/int/byte[]/long/float/double/Parcelable/Set<String>/string;

//數(shù)據(jù)的讀取和寫入關(guān)鍵是 nativeHandle 類似文件句柄

6.sp 遷移

public int importFromSharedPreferences(SharedPreferences preferences) {

? ? //獲取sp 存儲的所有鍵值對? 這里沒有對參數(shù)判空

? ? Map<String, ?> kvs = preferences.getAll();

? ? if (kvs != null && kvs.size() > 0) {

? ? ? ? Iterator var3 = kvs.entrySet().iterator();

? ? ? ? while(var3.hasNext()) {

? ? ? ? ? ? Entry<String, ?> entry = (Entry)var3.next();

? ? ? ? ? ? String key = (String)entry.getKey();

? ? ? ? ? ? Object value = entry.getValue();

? ? ? ? ? ? if (key != null && value != null) {

? ? ? ? ? ? ? ? if (value instanceof Boolean) {

? ? ? ? ? ? ? ? ? ? this.encodeBool(this.nativeHandle, key, (Boolean)value);

? ? ? ? ? ? ? ? } else if (value instanceof Integer) {

? ? ? ? ? ? ? ? ? ? this.encodeInt(this.nativeHandle, key, (Integer)value);

? ? ? ? ? ? ? ? } else if (value instanceof Long) {

? ? ? ? ? ? ? ? ? ? this.encodeLong(this.nativeHandle, key, (Long)value);

? ? ? ? ? ? ? ? } else if (value instanceof Float) {

? ? ? ? ? ? ? ? ? ? this.encodeFloat(this.nativeHandle, key, (Float)value);

? ? ? ? ? ? ? ? } else if (value instanceof Double) {

? ? ? ? ? ? ? ? ? ? this.encodeDouble(this.nativeHandle, key, (Double)value);

? ? ? ? ? ? ? ? } else if (value instanceof String) {

? ? ? ? ? ? ? ? ? ? this.encodeString(this.nativeHandle, key, (String)value);

? ? ? ? ? ? ? ? } else if (value instanceof Set) {

? ? ? ? ? ? ? ? ? ? this.encode(key, (Set)value);

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? simpleLog(MMKVLogLevel.LevelError, "unknown type: " + value.getClass());

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? //返回SP鍵值對的數(shù)量

? ? ? ? return kvs.size();

? ? } else {

? ? ? ? return 0;

? ? }

}

//sp 數(shù)據(jù)變更監(jiān)聽 這里MMKV沒有做實(shí)現(xiàn),調(diào)用會拋異常 謹(jǐn)慎調(diào)用

public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {

? ? ? ? throw new UnsupportedOperationException("Not implement in MMKV");

? ? }

? ? public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {

? ? ? ? throw new UnsupportedOperationException("Not implement in MMKV");

? ? }

7.日志定向/數(shù)據(jù)恢復(fù)

//默認(rèn)的log等級是levelInfo劲阎,轉(zhuǎn)換成相應(yīng)等級

private static int logLevel2Int(MMKVLogLevel level) {

? ? byte realLevel;

? ? switch(level) {

? ? case LevelDebug:

? ? ? ? realLevel = 0;

? ? ? ? break;

? ? case LevelWarning:

? ? ? ? realLevel = 2;

? ? ? ? break;

? ? case LevelError:

? ? ? ? realLevel = 3;

? ? ? ? break;

? ? case LevelNone:

? ? ? ? realLevel = 4;

? ? ? ? break;

? ? case LevelInfo:

? ? default:

? ? ? ? realLevel = 1;

? ? }

? ? return realLevel;

}

public static void setLogLevel(MMKVLogLevel level) {

? ? int realLevel = logLevel2Int(level);

? ? //設(shè)置給MMKV底層庫

? ? setLogLevel(realLevel);

}

//注冊gCallbackHandler

public static void registerHandler(MMKVHandler handler) {

? ? gCallbackHandler = handler;

? ? if (gCallbackHandler.wantLogRedirecting()) {

? ? ? ? setCallbackHandler(true, true);

? ? ? ? gWantLogReDirecting = true;

? ? } else {

? ? ? ? setCallbackHandler(false, true);

? ? ? ? gWantLogReDirecting = false;

? ? }

}

//解注冊gCallbackHandler

public static void unregisterHandler() {

? ? gCallbackHandler = null;

? ? setCallbackHandler(false, false);

? ? gWantLogReDirecting = false;

}

//文件CRC校驗(yàn)錯誤時 默認(rèn)采用丟棄策略绘盟,如果設(shè)置了gCallbackHandler,則采用設(shè)置的策略悯仙,要么丟棄龄毡,要么恢復(fù),恢復(fù)不可信

private static int onMMKVCRCCheckFail(String mmapID) {

? ? MMKVRecoverStrategic strategic = MMKVRecoverStrategic.OnErrorDiscard;

? ? if (gCallbackHandler != null) {

? ? ? ? strategic = gCallbackHandler.onMMKVCRCCheckFail(mmapID);

? ? }

? ? simpleLog(MMKVLogLevel.LevelInfo, "Recover strategic for " + mmapID + " is " + strategic);

? ? Integer value = (Integer)recoverIndex.get(strategic);

? ? return value == null ? 0 : value;

}

//文件長度錯誤時 默認(rèn)采用丟棄策略锡垄,如果設(shè)置了gCallbackHandler沦零,則采用設(shè)置的策略,要么丟棄要么恢復(fù)货岭,恢復(fù)不可信

private static int onMMKVFileLengthError(String mmapID) {

? ? MMKVRecoverStrategic strategic = MMKVRecoverStrategic.OnErrorDiscard;

? ? if (gCallbackHandler != null) {

? ? ? ? strategic = gCallbackHandler.onMMKVFileLengthError(mmapID);

? ? }


? ? simpleLog(MMKVLogLevel.LevelInfo, "Recover strategic for " + mmapID + " is " + strategic);

? ? Integer value = (Integer)recoverIndex.get(strategic);

? ? return value == null ? 0 : value;

}

//如果gCallbackHandler 設(shè)置了日志重定向 則重定向日志由業(yè)務(wù)方接管路操,否則MMKV采用默認(rèn)的Log 輸出

private static void mmkvLogImp(int level, String file, int line, String function, String message) {

? ? if (gCallbackHandler != null && gWantLogReDirecting) {

? ? ? ? gCallbackHandler.mmkvLog(index2LogLevel[level], file, line, function, message);

? ? } else {

? ? ? ? switch(index2LogLevel[level]) {

? ? ? ? case LevelDebug:

? ? ? ? ? ? Log.d("MMKV", message);

? ? ? ? ? ? break;

? ? ? ? case LevelWarning:

? ? ? ? ? ? Log.w("MMKV", message);

? ? ? ? ? ? break;

? ? ? ? case LevelError:

? ? ? ? ? ? Log.e("MMKV", message);

? ? ? ? case LevelNone:

? ? ? ? default:

? ? ? ? ? ? break;

? ? ? ? case LevelInfo:

? ? ? ? ? ? Log.i("MMKV", message);

? ? ? ? }

? ? }

}

//獲取堆棧信息 組織log? 函數(shù)內(nèi)沒有做越界判斷?

private static void simpleLog(MMKVLogLevel level, String message) {

? ? StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();

? ? StackTraceElement e = stacktrace[stacktrace.length - 1];

? ? Integer i = (Integer)logLevel2Index.get(level);

? ? int intLevel = i == null ? 0 : i;

? ? mmkvLogImp(intLevel, e.getFileName(), e.getLineNumber(), e.getMethodName(), message);

}


8.數(shù)據(jù)變化通知(進(jìn)程間使用)

//注冊數(shù)據(jù)變化監(jiān)聽

public static void registerContentChangeNotify(MMKVContentChangeNotification notify) {

? ? gContentChangeNotify = notify;

? ? setWantsContentChangeNotify(gContentChangeNotify != null);

}

//解注冊數(shù)據(jù)變化監(jiān)聽

public static void unregisterContentChangeNotify() {

? ? gContentChangeNotify = null;

? ? setWantsContentChangeNotify(false);

}

//其他進(jìn)程改變數(shù)據(jù)后通知

private static void onContentChangedByOuterProcess(String mmapID) {

? ? if (gContentChangeNotify != null) {

? ? ? ? gContentChangeNotify.onContentChangedByOuterProcess(mmapID);

? ? }

}

private static native void setWantsContentChangeNotify(boolean var0);

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市千贯,隨后出現(xiàn)的幾起案子屯仗,更是在濱河造成了極大的恐慌,老刑警劉巖丈牢,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祭钉,死亡現(xiàn)場離奇詭異,居然都是意外死亡己沛,警方通過查閱死者的電腦和手機(jī)慌核,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來申尼,“玉大人垮卓,你說我怎么就攤上這事∈δ唬” “怎么了粟按?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵诬滩,是天一觀的道長。 經(jīng)常有香客問我灭将,道長疼鸟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任庙曙,我火速辦了婚禮空镜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捌朴。我一直安慰自己吴攒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布砂蔽。 她就那樣靜靜地躺著洼怔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪左驾。 梳的紋絲不亂的頭發(fā)上镣隶,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機(jī)與錄音什荣,去河邊找鬼矾缓。 笑死怀酷,一個胖子當(dāng)著我的面吹牛稻爬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜕依,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼桅锄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了样眠?” 一聲冷哼從身側(cè)響起友瘤,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎檐束,沒想到半個月后辫秧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡被丧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年盟戏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甥桂。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡柿究,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出黄选,到底是詐尸還是另有隱情蝇摸,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站貌夕,受9級特大地震影響律歼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜啡专,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一苗膝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧植旧,春花似錦辱揭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至完沪,卻和暖如春域庇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背覆积。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工听皿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宽档。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓拆檬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親芬迄。 傳聞我的和親對象是個殘疾皇子嘲碧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容