[杞琞涓€鏂囪鎳?SharedPreferences 鐨勭己闄峰強涓€鐐圭偣鎬濊€?br> SharedPreferences 鏄郴缁熸彁渚涚殑涓€涓€傚悎鐢ㄤ簬瀛樺偍灏戦噺閿€煎鏁版嵁鐨勬寔涔呭寲瀛樺偍鏂规锛岀粨鏋勭畝鍗曪紝浣跨敤鏂逛究锛屽緢澶氬簲鐢ㄩ兘浼氫娇鐢ㄥ埌銆傚彟涓€鏂归潰锛孲haredPreferences 瀛樺湪鐨勯棶棰樹篃鎸哄鐨勶紝褰撲腑 ANR 闂灏卞薄瑙佷笉椴滐紝瀛楄妭璺冲姩鎶€鏈洟闃熷氨鏇剧粡鍙戝竷杩囦竴绡囨枃绔犱笓闂ㄦ潵闃愯堪璇ラ棶棰橈細鍓栨瀽 SharedPreference apply 寮曡捣鐨?ANR 闂銆傚埌浜嗙幇鍦紝Google Jetpack 涔熸帹鍑轰簡涓€濂楁柊鐨勬寔涔呭寲瀛樺偍鏂规锛欴ataStore锛屽ぇ鏈夊彇浠?SharedPreferences 鐨勮秼鍔?/p>
鏈枃灏辩粨鍚堟簮鐮佹潵鍓栨瀽 SharedPreferences 瀛樺湪鐨勭己闄蜂互鍙婅儗鍚庣殑鍏蜂綋鍘熷洜锛屽熀浜?SDK 30 杩涜鍒嗘瀽锛岃璇昏€呭仛鍒扮煡鍏剁劧涔熺煡鍏舵墍浠ョ劧锛屽苟鍦ㄦ渶鍚庝粙缁嶄笅鎴戜釜浜虹殑涓€绉嶅瓨鍌ㄦ満鍒惰璁℃柟妗堬紝甯屾湜瀵逛綘鏈夋墍甯姪.
涓嶅緱涓嶈鐨勫潙
浼氫竴鐩村崰鐢ㄥ唴瀛?/p>
SharedPreferences 鏈韩鏄竴涓帴鍙o紝鍏蜂綋鐨勫疄鐜扮被鏄?SharedPreferencesImpl锛孋ontext 涓悇涓拰 SP 鐩稿叧鐨勬柟娉曢兘鏄敱 ContextImpl 鏉ュ疄鐜扮殑銆傛垜浠」鐩腑鐨勬瘡涓?SP 鎴栧鎴栧皯閮芥槸淇濆瓨鐫€涓€浜涢敭鍊煎锛岃€屾瘡褰撴垜浠幏鍙栧埌涓€涓?SP 瀵硅薄鏃讹紝鍏跺搴旂殑鏁版嵁灏变細涓€鐩磋淇濈暀鍦ㄥ唴瀛樹腑锛岀洿鍒板簲鐢ㄨ繘绋嬭缁堢粨锛屽洜涓烘瘡涓?SP 瀵硅薄閮借绯荤粺浣滀负闈欐€佸彉閲忕紦瀛樿捣鏉ヤ簡锛屽搴?ContextImpl 涓殑闈欐€佸彉閲?sSharedPrefsCache
class ContextImpl extends Context {
//鍏堟牴鎹簲鐢ㄥ寘鍚嶇紦瀛樻墍鏈?SharedPreferences
//鍐嶆牴鎹?xmlFile 鍜屽叿浣撶殑 SharedPreferencesImpl 瀵瑰簲涓?
private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;
//鏍规嵁 fileName 鎷垮埌瀵瑰簲鐨?xmlFile
private ArrayMap<String, File> mSharedPrefsPaths;
}
姣忎釜 SP 閮藉搴斾竴涓湰鍦扮鐩樹腑鐨?xmlFile锛宖ileName 鍒欐槸鐢卞紑鍙戣€呮潵鏄惧紡鎸囧畾鐨勶紝姣忎釜 xmlFile 閮藉搴斾竴涓?SharedPreferencesImpl銆傛墍浠?ContextImpl 鐨勯€昏緫鏄厛鏍规嵁 fileName 鎷垮埌 xmlFile锛屽啀鏍规嵁 xmlFile 鎷垮埌 SharedPreferencesImpl锛屾渶缁堝簲鐢ㄥ唴鎵€鏈夌殑 SharedPreferencesImpl 灏遍兘浼氳缂撳瓨鍦?sSharedPrefsCache
杩欎釜闈欐€佸彉閲忎腑銆傛澶栵紝鐢变簬 SharedPreferencesImpl 鍦ㄥ垵濮嬪寲鍚庡氨浼氳嚜鍔ㄥ幓鍔犺浇 xmlFile 涓殑鎵€鏈夐敭鍊煎鏁版嵁锛岃€?ContextImpl 鍐呴儴骞舵病鏈夌湅鍒版湁娓呯悊 sSharedPrefsCache
缂撳瓨鐨勯€昏緫锛屾墍浠?sSharedPrefsCache
浼氳涓€鐩翠繚鐣欏湪鍐呭瓨涓洿鍒拌繘绋嬬粓缁擄紝鍏跺唴瀛樺ぇ灏忎細闅忕潃鎴戜滑寮曠敤鍒扮殑 SP 澧炲鑰屽姞澶э紝杩欏氨鍙兘浼氭寔缁崰鐢ㄥ緢澶т竴鍧楀唴瀛樼┖闂?/p>
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
路路路
File file;
synchronized (ContextImpl.class) {
if (mSharedPrefsPaths == null) {
mSharedPrefsPaths = new ArrayMap<>();
}
file = mSharedPrefsPaths.get(name);
if (file == null) {
file = getSharedPreferencesPath(name);
mSharedPrefsPaths.put(name, file);
}
}
return getSharedPreferences(file, mode);
}
@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
sp = cache.get(file);
if (sp == null) {
路路路
sp = new SharedPreferencesImpl(file, mode);
cache.put(file, sp);
return sp;
}
}
路路路
return sp;
}
@GuardedBy("ContextImpl.class")
private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
if (sSharedPrefsCache == null) {
sSharedPrefsCache = new ArrayMap<>();
}
final String packageName = getPackageName();
ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefsCache.get(packageName);
if (packagePrefs == null) {
packagePrefs = new ArrayMap<>();
sSharedPrefsCache.put(packageName, packagePrefs);
}
return packagePrefs;
}
GetValue 鍙兘瀵艰嚧绾跨▼闃诲
SharedPreferencesImpl 鍦ㄦ瀯閫犲嚱鏁颁腑鐩存帴灏卞惎鍔ㄤ簡涓€涓瓙绾跨▼鍘诲姞杞界鐩樻枃浠讹紝杩欐剰鍛崇潃璇ユ搷浣滄槸涓€涓紓姝ユ搷浣滐紙鎴戝ソ鍍忓湪璇村簾璇濓級锛屽鏋滄枃浠跺緢澶ф垨鑰呯嚎绋嬭皟搴︾郴缁熸病鏈夐┈涓婂惎鍔ㄨ绾跨▼鐨勮瘽锛岄偅涔堣鎿嶄綔灏遍渶瑕佷竴灏忔鏃堕棿鍚庢墠鑳芥墽琛屽畬姣?/p>
final class SharedPreferencesImpl implements SharedPreferences {
@UnsupportedAppUsage
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
mThrowable = null;
startLoadFromDisk();
}
@UnsupportedAppUsage
private void startLoadFromDisk() {
synchronized (mLock) {
mLoaded = false;
}
new Thread("SharedPreferencesImpl-load") {
public void run() {
//鍔犺浇纾佺洏鏂囦欢
loadFromDisk();
}
}.start();
}
}
鑰屽鏋滄垜浠湪鍒濆鍖?SharedPreferencesImpl 鍚庣揣鎺ョ潃灏卞幓 getValue 鐨勮瘽锛屽娍蹇呬篃闇€瑕佺‘淇濆瓙绾跨▼宸茬粡鍔犺浇瀹屾垚鍚庢墠鍘昏繘琛屽彇鍊兼搷浣滐紝鎵€浠?SharedPreferencesImpl 灏遍€氳繃鍦ㄦ瘡涓?getValue 鏂规硶涓皟鐢?awaitLoadedLocked()
鏂规硶鏉ュ垽鏂槸鍚﹂渶瑕侀樆濉炲閮ㄧ嚎绋嬶紝纭繚鍙栧€兼搷浣滀竴瀹氫細鍦ㄥ瓙绾跨▼鎵ц瀹屾瘯鍚庢墠鎵ц銆?code>loadFromDisk()鏂规硶浼氬湪浠诲姟鎵ц瀹屾瘯鍚庤皟鐢?mLock.notifyAll()
鍞ら啋鎵€鏈夎闃诲鐨勭嚎绋?/p>
@Override
@Nullable
public String getString(String key, @Nullable String defValue) {
synchronized (mLock) {
//鍒ゆ柇鏄惁闇€瑕佽澶栭儴绾跨▼绛夊緟
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null v : defValue;
}
}
@GuardedBy("mLock")
private void awaitLoadedLocked() {
if (!mLoaded) {
BlockGuard.getThreadPolicy().onReadFromDisk();
}
while (!mLoaded) {
try {
//杩樻湭鍔犺浇绾跨▼锛岃澶栭儴绾跨▼鏆傚仠绛夊緟
mLock.wait();
} catch (InterruptedException unused) {
}
}
if (mThrowable != null) {
throw new IllegalStateException(mThrowable);
}
}
private void loadFromDisk() {
路路路
synchronized (mLock) {
mLoaded = true;
mThrowable = thrown;
try {
if (thrown == null) {
if (map != null) {
mMap = map;
mStatTimestamp = stat.st_mtim;
mStatSize = stat.st_size;
} else {
mMap = new HashMap<>();
}
}
} catch (Throwable t) {
mThrowable = t;
} finally {
//鍞ら啋鎵€鏈夎闃诲鐨勭嚎绋?
mLock.notifyAll();
}
}
}
鎵€浠ヨ锛屽鏋?SP 瀛樺偍鐨勬暟鎹噺寰堝ぇ鐨勮瘽锛岄偅涔堝氨鏈夊彲鑳藉鑷村閮ㄧ殑璋冪敤鑰呯嚎绋嬭闃诲锛屼弗閲嶆椂鐢氳嚦鍙兘瀵艰嚧 ANR銆傚綋鐒讹紝杩欑鍙兘鎬т篃鍙槸鍙戠敓鍦ㄥ姞杞界鐩樻枃浠跺畬鎴愪箣鍓嶏紝褰撳姞杞藉畬鎴愬悗 awaitLoadedLocked()
鏂规硶鑷劧涓嶄細闃诲绾跨▼
GetValue 涓嶄繚璇佹暟鎹被鍨嬪畨鍏?/p>
浠ヤ笅浠g爜鍦ㄧ紪璇戦樁娈垫槸瀹屽叏姝e父鐨勶紝浣嗗湪杩愯鏃跺氨浼氭姏鍑哄紓甯革細java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
銆傚緢鏄庢樉锛岃繖鏄敱浜庡悓涓?key 鍏堝悗瀵瑰簲浜嗕笉鍚屾暟鎹被鍨嬪鑷寸殑锛孲haredPreferences 娌℃湁鍔炴硶瀵硅繖绉嶆搷浣滃仛鍑洪檺鍒讹紝瀹屽叏闇€瑕佷緷璧栦簬寮€鍙戣€呰嚜宸辩殑浠g爜瑙勮寖鏉ヨ繘琛岄檺鍒?/p>
val sharedPreferences: SharedPreferences = getSharedPreferences("UserInfo", Context.MODE_PRIVATE)
val key = "userName"
val edit = sharedPreferences.edit()
edit.putInt(key, 11)
edit.apply()
val name = sharedPreferences.getString(key, "")
涓嶆敮鎸佸杩涚▼鏁版嵁鍏变韩
鍦ㄨ幏鍙?SP 瀹炰緥鐨勬椂鍊欓渶瑕佷紶鍏ヤ竴涓?int 绫诲瀷鐨?mode 鏍囪浣嶅弬鏁帮紝瀛樺湪涓€涓拰澶氳繘绋嬬浉鍏崇殑鏍囪浣?MODE_MULTI_PROCESS锛岃鏍囪浣嶈兘璧峰埌涓€瀹氱▼搴︾殑澶氳繘绋嬫暟鎹悓姝ョ殑淇濋殰锛屼絾浣滅敤涓嶅ぇ锛屼笖骞朵笉淇濊瘉澶氳繘绋嬪苟鍙戝畨鍏ㄦ€?/p>
val sharedPreferences: SharedPreferences = getSharedPreferences("UserInfo", Context.MODE_MULTI_PROCESS)
涓婃枃鏈夎鍒帮紝SharedPreferencesImpl 鍦ㄨ鍔犺浇鍚庡氨浼氫竴鐩翠繚鐣欏湪鍐呭瓨涓紝涔嬪悗姣忔鑾峰彇閮芥槸鐩存帴浣跨敤缂撳瓨鏁版嵁锛岄€氬父鎯呭喌涓嬩篃涓嶄細鍐嶆鍘诲姞杞界鐩樻枃浠躲€傝€?MODE_MULTI_PROCESS 璧峰埌鐨勪綔鐢ㄥ氨鏄瘡褰撳啀涓€娆″幓鑾峰彇 SP 瀹炰緥鏃讹紝浼氬垽鏂綋鍓嶇鐩樻枃浠剁浉瀵规渶鍚庝竴娆″唴瀛樹慨鏀规槸鍚﹁鏀瑰姩杩囦簡锛屽鏋滄槸鐨勮瘽灏变富鍔ㄥ幓閲嶆柊鍔犺浇纾佺洏鏂囦欢锛屼粠鑰屽彲浠ュ仛鍒板湪澶氳繘绋嬬幆澧冧笅涓€瀹氱殑鏁版嵁鍚屾
浣嗘槸锛岃繖绉嶅悓姝ユ湰韬綔鐢ㄤ笉澶э紝鍥犱负鍗充娇姝ゆ椂閲嶆柊鍔犺浇纾佺洏鏂囦欢浜嗭紝鍚庣画淇敼 SP 鍊兼椂涓嶅悓杩涚▼涓殑鍐呭瓨鏁版嵁涔熶笉浼氬疄鏃跺悓姝ワ紝涓斿杩涚▼鍚屾椂淇敼 SP 鍊间篃瀛樺湪鏁版嵁涓㈠け鍜屾暟鎹鐩栫殑鍙兘銆傛墍浠ヨ锛孲P 骞朵笉鏀寔澶氳繘绋嬫暟鎹叡浜紝MODE_MULTI_PROCESS 涔熷凡缁忚搴熷純浜嗭紝鍏舵敞閲婁篃鎺ㄨ崘浣跨敤 ContentProvider 鏉ュ疄鐜拌法杩涚▼閫氫俊
class ContextImpl extends Context {
@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
路路路
}
if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
//閲嶆柊鍘诲姞杞界鐩樻枃浠?
sp.startReloadIfChangedUnexpectedly();
}
return sp;
}
}
涓嶆敮鎸佸閲忔洿鏂?/p>
鎴戜滑鐭ラ亾锛孲P 鎻愪氦鏁版嵁鐨勬柟娉曟湁涓や釜锛?code>commit() 鍜?apply()
锛屽垎鍒搴旂潃鍚屾淇敼鍜屽紓姝ヤ慨鏀癸紝鑰岃繖涓ょ鏂瑰紡瀵瑰簲鐨勯兘鏄叏閲忔洿鏂帮紝SP 浠ユ枃浠朵负鏈€灏忓崟浣嶈繘琛屼慨鏀癸紝鍗充娇鎴戜滑鍙慨鏀逛簡涓€涓敭鍊煎锛岃繖涓や釜鏂规硶涔熶細灏嗘墍鏈夐敭鍊煎鏁版嵁閲嶆柊鍐欏叆鍒扮鐩樻枃浠朵腑锛屽嵆 SP 鍙敮鎸佸叏閲忔洿鏂?br>
鎴戜滑骞虫椂鑾峰彇鍒扮殑 Editor 瀵硅薄锛屽搴旂殑閮芥槸 SharedPreferencesImpl 鐨勫唴閮ㄧ被 EditorImpl銆侲ditorImpl 鐨勬瘡涓?putValue 鏂规硶閮戒細灏嗕紶杩涙潵鐨?key-value 淇濆瓨鍦?mModified
涓紝鏆傛椂杩樻病鏈夋秹鍙婁换浣曟枃浠舵敼鍔ㄣ€傛瘮杈冪壒娈婄殑鏄?remove
鍜?clear
涓や釜鏂规硶锛?code>remove 鏂规硶浼氬皢 this 浣滀负閿€煎鐨?value锛屽悗缁氨閫氳繃瀵规瘮 value 鐨勭浉绛夋€ф潵鐭ラ亾鏄绉婚櫎閿€煎杩樻槸淇敼閿€煎锛?code>clear 鏂规硶鍒欏彧鏄皢 mClear 鏍囪浣嶇疆涓?true
public final class EditorImpl implements Editor {
private final Object mEditorLock = new Object();
@GuardedBy("mEditorLock")
private final Map<String, Object> mModified = new HashMap<>();
@GuardedBy("mEditorLock")
private boolean mClear = false;
@Override
public Editor putString(String key, @Nullable String value) {
synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
@Override
public Editor remove(String key) {
synchronized (mEditorLock) {
//瀛樺叆褰撳墠鐨?EditorImpl 瀵硅薄
mModified.put(key, this);
return this;
}
}
@Override
public Editor clear() {
synchronized (mEditorLock) {
mClear = true;
return this;
}
}
}
commit()
鍜?code>apply()涓や釜鏂规硶閮戒細閫氳繃璋冪敤 commitToMemory()
鏂规硶鎷垮埌淇敼鍚庣殑鍏ㄩ噺鏁版嵁commitToMemory()
閲囩敤浜?diff 绠楁硶锛孲P 鍖呭惈鐨勬墍鏈夐敭鍊煎鏁版嵁閮藉瓨鍌ㄥ湪 mapToWriteToDisk 涓紝Editor 鏀瑰姩鍒扮殑鎵€鏈夐敭鍊煎鏁版嵁閮藉瓨鍌ㄥ湪 mModified 涓€傚鏋? mClear 涓?true锛屽垯浼氬厛娓呯┖ mapToWriteToDisk锛岀劧鍚庡啀閬嶅巻 mModified锛屽皢 mModified 涓殑鎵€鏈夋敼鍔ㄩ兘鍚屾缁?mapToWriteToDisk銆傛渶缁?mapToWriteToDisk 灏变繚瀛樹簡瑕侀噸鏂板啓鍏ュ埌纾佺洏鏂囦欢涓殑鍏ㄩ噺鏁版嵁锛孲P 浼氭牴鎹?mapToWriteToDisk 瀹屽叏瑕嗙洊鎺夋棫鐨?xml 鏂囦欢
// Returns true if any changes were made
private MemoryCommitResult commitToMemory() {
long memoryStateGeneration;
boolean keysCleared = false;
List<String> keysModified = null;
Set<OnSharedPreferenceChangeListener> listeners = null;
Map<String, Object> mapToWriteToDisk;
synchronized (SharedPreferencesImpl.this.mLock) {
// We optimistically don't make a deep copy until
// a memory commit comes in when we're already
// writing to disk.
if (mDiskWritesInFlight > 0) {
// We can't modify our mMap as a currently
// in-flight write owns it. Clone it before
// modifying it.
// noinspection unchecked
mMap = new HashMap<String, Object>(mMap);
}
//鎷垮埌鍐呭瓨涓殑鍏ㄩ噺鏁版嵁
mapToWriteToDisk = mMap;
mDiskWritesInFlight++;
boolean hasListeners = mListeners.size() > 0;
if (hasListeners) {
keysModified = new ArrayList<String>();
listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
}
synchronized (mEditorLock) {
//鐢ㄤ簬鏍囪鏈€缁堟槸鍚︽敼鍔ㄥ埌浜?mapToWriteToDisk
boolean changesMade = false;
if (mClear) {
if (!mapToWriteToDisk.isEmpty()) {
changesMade = true;
//娓呯┖鎵€鏈夊湪鍐呭瓨涓殑鏁版嵁
mapToWriteToDisk.clear();
}
keysCleared = true;
//鎭㈠鐘舵€侊紝閬垮厤浜屾淇敼鏃剁姸鎬侀敊浣?
mClear = false;
}
for (Map.Entry<String, Object> e : mModified.entrySet()) {
String k = e.getKey();
Object v = e.getValue();
// "this" is the magic value for a removal mutation. In addition,
// setting a value to "null" for a given key is specified to be
// equivalent to calling remove on that key.
if (v == this || v == null) { //鎰忓懗鐫€瑕佺Щ闄よ閿€煎
if (!mapToWriteToDisk.containsKey(k)) {
continue;
}
mapToWriteToDisk.remove(k);
} else { //瀵瑰簲淇敼閿€煎鍊肩殑鎯呭喌
if (mapToWriteToDisk.containsKey(k)) {
Object existingValue = mapToWriteToDisk.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
//鍙湁鍦ㄧ殑纭槸淇敼浜嗘垨鏂版彃鍏ラ敭鍊煎鐨勬儏鍐垫墠闇€瑕佷繚瀛樺€?
mapToWriteToDisk.put(k, v);
}
changesMade = true;
if (hasListeners) {
keysModified.add(k);
}
}
//鎭㈠鐘舵€侊紝閬垮厤浜屾淇敼鏃剁姸鎬侀敊浣?
mModified.clear();
if (changesMade) {
mCurrentMemoryStateGeneration++;
}
memoryStateGeneration = mCurrentMemoryStateGeneration;
}
}
return new MemoryCommitResult(memoryStateGeneration, keysCleared, keysModified,
listeners, mapToWriteToDisk);
}
Clear 鐨勫弽鐩磋鐢ㄦ硶
鐪嬩互涓嬩緥瀛愩€傛寜鐓ц涔夊垎鏋愮殑璇濓紝鏈€缁?SP 涓簲璇ユ槸鍙墿涓?blog 涓€涓敭鍊煎鎵嶇鍚堢洿瑙夛紝鑰屽疄闄呬笂鏈€缁堜袱涓敭鍊煎閮戒細琚繚鐣欙紝涓斿彧鏈夎繖涓や釜閿€煎琚繚鐣欎笅鏉?/p>
val sharedPreferences: SharedPreferences = getSharedPreferences("UserInfo", Context.MODE_PRIVATE)
val edit = sharedPreferences.edit()
edit.putString("name", "涓氬織闄?).clear().putString("blog", "https://juejin.cn/user/923245496518439")
edit.apply()
閫犳垚璇ラ棶棰樼殑鍘熷洜杩橀渶瑕佺湅commitToMemory()
鏂规硶銆?code>clear()浼氬皢 mClear 缃负 true锛屾墍浠ュ湪鎵ц鍒扮涓€姝ョ殑鏃跺€欏氨浼氬皢鍐呭瓨涓殑鎵€鏈夐敭鍊煎鏁版嵁 mapToWriteToDisk 娓呯┖銆傚綋鎵ц鍒扮浜屾鐨勬椂鍊欙紝mModified 涓殑鎵€鏈夋暟鎹氨閮戒細鍚屾鍒?mapToWriteToDisk 涓紝浠庤€屽鑷存渶缁?name 鍜?blog 涓や釜閿€煎閮戒細琚繚鐣欎笅鏉ワ紝鍏跺畠閿€煎閮借绉婚櫎浜?br>
鎵€浠ヨ锛?code>Editor.clear() 涔嬪墠涓嶅簲璇ヨ繛璐皟鐢?putValue 璇彞锛岃繖浼氶€犳垚鐞嗚В鍜屽疄闄呮晥鏋滀箣闂寸殑鍋忓樊
// Returns true if any changes were made
private MemoryCommitResult commitToMemory() {
long memoryStateGeneration;
boolean keysCleared = false;
List<String> keysModified = null;
Set<OnSharedPreferenceChangeListener> listeners = null;
Map<String, Object> mapToWriteToDisk;
synchronized (SharedPreferencesImpl.this.mLock) {
// We optimistically don't make a deep copy until
// a memory commit comes in when we're already
// writing to disk.
if (mDiskWritesInFlight > 0) {
// We can't modify our mMap as a currently
// in-flight write owns it. Clone it before
// modifying it.
// noinspection unchecked
mMap = new HashMap<String, Object>(mMap);
}
//鎷垮埌鍐呭瓨涓殑鍏ㄩ噺鏁版嵁
mapToWriteToDisk = mMap;
mDiskWritesInFlight++;
boolean hasListeners = mListeners.size() > 0;
if (hasListeners) {
keysModified = new ArrayList<String>();
listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
}
synchronized (mEditorLock) {
boolean changesMade = false;
if (mClear) { //绗竴姝?
if (!mapToWriteToDisk.isEmpty()) {
changesMade = true;
//娓呯┖鎵€鏈夊湪鍐呭瓨涓殑鏁版嵁
mapToWriteToDisk.clear();
}
keysCleared = true;
//鎭㈠鐘舵€侊紝閬垮厤浜屾淇敼鏃剁姸鎬侀敊浣?
mClear = false;
}
for (Map.Entry<String, Object> e : mModified.entrySet()) { //绗簩姝?
String k = e.getKey();
Object v = e.getValue();
// "this" is the magic value for a removal mutation. In addition,
// setting a value to "null" for a given key is specified to be
// equivalent to calling remove on that key.
if (v == this || v == null) { //鎰忓懗鐫€瑕佺Щ闄よ閿€煎
if (!mapToWriteToDisk.containsKey(k)) {
continue;
}
mapToWriteToDisk.remove(k);
} else { //瀵瑰簲淇敼閿€煎鍊肩殑鎯呭喌
if (mapToWriteToDisk.containsKey(k)) {
Object existingValue = mapToWriteToDisk.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
//鍙湁鍦ㄧ殑纭槸淇敼浜嗘垨鏂版彃鍏ラ敭鍊煎鐨勬儏鍐垫墠闇€瑕佷繚瀛樺€?
mapToWriteToDisk.put(k, v);
}
changesMade = true;
if (hasListeners) {
keysModified.add(k);
}
}
//鎭㈠鐘舵€侊紝閬垮厤浜屾淇敼鏃剁姸鎬侀敊浣?
mModified.clear();
if (changesMade) {
mCurrentMemoryStateGeneration++;
}
memoryStateGeneration = mCurrentMemoryStateGeneration;
}
}
return new MemoryCommitResult(memoryStateGeneration, keysCleared, keysModified,
listeners, mapToWriteToDisk);
}
Commit銆乤pply 鍙兘瀵艰嚧 ANR
commit()
鏂规硶浼氶€氳繃 commitToMemory()
鏂规硶鎷垮埌鏈淇敼鍚庣殑鍏ㄩ噺鏁版嵁锛屽嵆 MemoryCommitResult锛岀劧鍚庡悜 enqueueDiskWrite
鏂规硶鎻愪氦灏嗗叏閲忔暟鎹啓鍏ョ鐩樻枃浠剁殑浠诲姟锛屽湪鍐欏叆瀹屾垚鍓嶈皟鐢ㄨ€呯嚎绋嬮兘浼氱敱浜?CountDownLatch 涓€鐩撮樆濉炵瓑寰呯潃锛屾柟娉曡繑鍥炲€煎嵆鏈淇敼鎿嶄綔鐨勬垚鍔熺姸鎬?/p>
@Override
public boolean commit() {
long startTime = 0;
if (DEBUG) {
startTime = System.currentTimeMillis();
}
//鎷垮埌淇敼鍚庣殑鍏ㄩ噺鏁版嵁
MemoryCommitResult mcr = commitToMemory();
//鎻愪氦鍐欏叆纾佺洏鏂囦欢鐨勪换鍔?
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);
try {
//闃诲绛夊緟锛岀洿鍒?xml 鏂囦欢鍐欏叆瀹屾垚锛堜笉绠℃垚鍔熶笌鍚︼級
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
} finally {
if (DEBUG) {
Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
+ " committed after " + (System.currentTimeMillis() - startTime)
+ " ms");
}
}
notifyListeners(mcr);
return mcr.writeToDiskResult;
}
enqueueDiskWrite
鏂规硶灏辨槸鍖呭惈浜嗗叿浣撶殑纾佺洏鍐欏叆閫昏緫鐨勫湴鏂逛簡锛岀敱浜庡閮ㄥ彲鑳藉瓨鍦ㄥ涓嚎绋嬪湪鍚屾椂鎵ц apply()
鍜?commit()
涓や釜鏂规硶锛岃€屽搴旂殑纾佺洏鏂囦欢鍙湁涓€涓紝鎵€浠?enqueueDiskWrite
鏂规硶灏卞繀椤讳繚璇佸啓鍏ユ搷浣滅殑鏈夊簭鎬э紝閬垮厤鏁版嵁涓㈠け鎴栬€呰鐩栵紝鐢氳嚦鏄枃浠舵崯鍧?br>
enqueueDiskWrite
鏂规硶鐨勫叿浣撻€昏緫锛?/p>
- writeToDiskRunnable 浣跨敤鍒颁簡鍐呴儴閿?mWritingToDiskLock 鏉ヤ繚璇?writeToFile 鎿嶄綔鐨勬湁搴忔€э紝閬垮厤澶氱嚎绋嬬珵浜?/li>
- 瀵逛簬 commit 鎿嶄綔锛屽鏋滃綋鍓嶅彧鏈変竴涓嚎绋嬪湪鎵ц鎻愪氦淇敼鐨勬搷浣滅殑璇濓紝閭d箞鐩存帴鍦ㄨ绾跨▼涓婃墽琛?writeToDiskRunnable锛屾祦绋嬬粨鏉?/li>
- 瀵逛簬鍏朵粬鎯呭喌锛坅pply 鎿嶄綔銆佸绾跨▼鍚屾椂 commit 鎴栬€?apply锛夛紝閮戒細灏?writeToDiskRunnable 鎻愪氦缁?QueuedWork 鎵ц
- QueuedWork 鍐呴儴浣跨敤鍒颁簡 HandlerThread 鏉ユ墽琛?writeToDiskRunnable锛孒andlerThread 鏈韩涔熷彲浠ヤ繚璇佸涓换鍔℃墽琛屾椂鐨勬湁搴忔€?/li>
private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {
final boolean isFromSyncCommit = (postWriteRunnable == null);
final Runnable writeToDiskRunnable = new Runnable() {
@Override
public void run() {
synchronized (mWritingToDiskLock) {
//鍐欏叆纾佺洏鏂囦欢
writeToFile(mcr, isFromSyncCommit);
}
synchronized (mLock) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
// Typical #commit() path with fewer allocations, doing a write on
// the current thread.
if (isFromSyncCommit) { //commit() 鏂规硶浼氳蛋杩涜繖閲岄潰
boolean wasEmpty = false;
synchronized (mLock) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
//wasEmpty 涓?true 璇存槑褰撳墠鍙湁涓€涓嚎绋嬪湪鎵ц鎻愪氦鎿嶄綔锛岄偅涔堝氨鐩存帴鍦ㄦ绾跨▼涓婂畬鎴愪换鍔?
writeToDiskRunnable.run();
return;
}
}
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}
姝ゅ锛岃繕鏈変竴涓瘮杈冮噸瑕佺殑鐭ヨ瘑鐐归渶瑕佹敞鎰忎笅銆傚湪 writeToFile 鏂规硶涓細瀵规湰娆′换鍔¤繘琛屾牎楠岋紝閬垮厤杩炵画澶氭鎵ц鏃犳晥鐨勭鐩樹换鍔°€傚綋涓紝mDiskStateGeneration 浠h〃鐨勬槸鏈€鍚庝竴娆℃垚鍔熷啓鍏ョ鐩樻枃浠舵椂鐨勪换鍔$増鏈彿锛宮CurrentMemoryStateGeneration 鏄綋鍓嶅唴瀛樹腑鏈€鏂扮殑淇敼璁板綍鐗堟湰鍙凤紝mcr.memoryStateGeneration 鏄湰娆¤鎵ц鐨勪换鍔$殑鐗堟湰鍙枫€傞€氳繃涓ゆ鐗堟湰鍙风殑瀵规瘮锛屽氨閬垮厤浜嗗湪杩炵画澶氭 commit 鎴栬€?apply 鏃堕€犳垚閲嶅鎵ц I/O 鎿嶄綔鐨勬儏鍐碉紝鑰屾槸鍙細鎵ц鏈€鍚庝竴娆★紝閬垮厤浜嗘棤鏁堢殑 I/O 浠诲姟
@GuardedBy("mWritingToDiskLock")
private void writeToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) {
路路路
if (fileExists) {
boolean needsWrite = false;
// Only need to write if the disk state is older than this commit
//鍒ゆ柇鐗堟湰鍙?
if (mDiskStateGeneration < mcr.memoryStateGeneration) {
if (isFromSyncCommit) {
needsWrite = true;
} else {
synchronized (mLock) {
// No need to persist intermediate states. Just wait for the latest state to
// be persisted.
//鍒ゆ柇鐗堟湰鍙?
if (mCurrentMemoryStateGeneration == mcr.memoryStateGeneration) {
needsWrite = true;
}
}
}
}
if (!needsWrite) {
//褰撳墠鐗堟湰鍙峰苟闈炴渶鏂帮紝鏃犻渶鎵ц锛岀洿鎺ヨ繑鍥炲嵆鍙?
mcr.setDiskWriteResult(false, true);
return;
}
路路路
}
鍐嶅洖杩囧ご鐪?commit()
鏂规硶銆備笉绠¤鏂规硶鍏宠仈鐨?writeToDiskRunnable 鏈€缁堟槸鍦ㄦ湰绾跨▼杩樻槸 HandlerThread 涓墽琛岋紝await()
鏂规硶閮戒細浣垮緱鏈嚎绋嬮樆濉炵瓑寰呯洿鍒?writeToDiskRunnable 鎵ц瀹屾瘯锛屼粠鑰屽疄鐜颁簡 commit()
鍚屾鎻愪氦鐨勬晥鏋?/p>
@Override
public boolean commit() {
long startTime = 0;
if (DEBUG) {
startTime = System.currentTimeMillis();
}
//鎷垮埌淇敼鍚庣殑鍏ㄩ噺鏁版嵁
MemoryCommitResult mcr = commitToMemory();
//鎻愪氦鍐欏叆纾佺洏鏂囦欢鐨勪换鍔?
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);
try {
//闃诲绛夊緟锛岀洿鍒?xml 鏂囦欢鍐欏叆瀹屾垚锛堜笉绠℃垚鍔熶笌鍚︼級
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
} finally {
if (DEBUG) {
Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
+ " committed after " + (System.currentTimeMillis() - startTime)
+ " ms");
}
}
notifyListeners(mcr);
return mcr.writeToDiskResult;
}
鑰屽浜?apply()
鏂规硶锛屽叾鏈韩鍏锋湁寮傛鎻愪氦鐨勫惈涔夛紝I/O 鎿嶄綔搴旇閮芥槸浜ょ敱缁欎簡瀛愮嚎绋嬫潵鎵ц鎵嶅锛屾寜閬撶悊鏉ヨ鍙渶瑕佽皟鐢?enqueueDiskWrite
鏂规硶鎻愪氦浠诲姟涓斾笉绛夊緟浠诲姟瀹屾垚鍗冲彲锛屽彲瀹為檯涓?code>apply()鏂规硶鍙嶈€岃姣?code>commit()鏂规硶澶嶆潅寰楀
apply()
鏂规硶鍖呭惈涓€涓?awaitCommit 浠诲姟锛岀敤浜庨樆濉炲叾鎵ц绾跨▼鐩村埌纾佺洏浠诲姟鎵ц瀹屾瘯锛岃€?awaitCommit 鍙堣鍖呰9鍦?postWriteRunnable 涓竴璧锋彁浜ょ粰浜?enqueueDiskWrite
鏂规硶锛?code>enqueueDiskWrite 鏂规硶鍙堜細鍦?writeToDiskRunnable 鎵ц瀹屾瘯鍚庢墽琛?enqueueDiskWrite
@Override
public void apply() {
final long startTime = System.currentTimeMillis();
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
@Override
public void run() {
try {
//闃诲绾跨▼鐩村埌纾佺洏浠诲姟鎵ц瀹屾瘯
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
if (DEBUG && mcr.wasWritten) {
Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
+ " applied after " + (System.currentTimeMillis() - startTime)
+ " ms");
}
}
};
QueuedWork.addFinisher(awaitCommit);
Runnable postWriteRunnable = new Runnable() {
@Override
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);
}
};
//鎻愪氦浠诲姟
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
// Okay to notify the listeners before it's hit disk
// because the listeners should always get the same
// SharedPreferences instance back, which has the
// changes reflected in memory.
notifyListeners(mcr);
}
鍗曠嫭鐪嬩互涓婇€昏緫浼氭樉寰楀崄鍒嗗鎬紝浠庝笂鏂囧氨鍙互寰楃煡 writeToDiskRunnable 鏈€缁堟槸浼氫氦鐢?HandlerThread 鏉ユ墽琛岀殑锛岄偅鎸夌収娴佺▼鐪?awaitCommit 鏈€缁堜篃鏄細鐢?HandlerThread 璋冪敤锛岄偅涔?awaitCommit 鐨勭瓑寰呮搷浣滃氨鏄惧緱鍗佸垎濂囨€簡锛屽洜涓?awaitCommit 鑲畾鏄細鍦ㄧ鐩樹换鍔℃墽琛屽畬姣曟墠琚皟鐢紝灏辩浉褰撲簬 HandlerThread 鍦ㄨ嚜宸辩瓑寰呰嚜宸辨墽琛屽畬姣曘€傛澶栵紝HandlerThread 灞炰簬瀛愮嚎绋嬶紝鎸夐亾鐞嗘潵璇村瓙绾跨▼鍗充娇鎵ц浜嗚€楁椂鎿嶄綔涔熶笉浼氬鑷翠富绾跨▼ ANR 鎵嶅
瑕佺悊瑙d互涓婃搷浣滐紝杩橀渶瑕佸啀鐪嬬湅 ActivityThread 杩欎釜绫汇€傚綋 Service 鍜?Activity 鐨勭敓鍛藉懆鏈熷浜?handleStopService()
銆?code>handlePauseActivity() 銆?code>handleStopActivity() 鐨勬椂鍊欙紝ActivityThread 浼氳皟鐢?QueuedWork.waitToFinish()
鏂规硶
private void handleStopService(IBinder token) {
Service s = mServices.remove(token);
if (s != null) {
try {
路路路
//閲嶇偣
QueuedWork.waitToFinish();
路路路
} catch (Exception e) {
路路路
}
} else {
Slog.i(TAG, "handleStopService: token=" + token + " not found.");
}
//Slog.i(TAG, "Running services: " + mServices);
}
QueuedWork.waitToFinish()
鏂规硶浼氫富鍔ㄥ幓鎵ц鎵€鏈夌殑纾佺洏鍐欏叆浠诲姟锛屽苟鎵ц鎵€鏈夌殑 postWriteRunnable锛岃繖灏遍€犳垚浜?Activity 鎴?Service 鍦ㄥ垏鎹㈢敓鍛藉懆鏈熺殑杩囩▼涓湁鍙兘鍥犱负瀛樺湪澶ч噺鐨勭鐩樺啓鍏ヤ换鍔¤€岃闃诲浣忥紝鏈€缁堝鑷?ANR
public static void waitToFinish() {
long startTime = System.currentTimeMillis();
boolean hadMessages = false;
Handler handler = getHandler();
synchronized (sLock) {
if (handler.hasMessages(QueuedWorkHandler.MSG_RUN)) {
// Delayed work will be processed at processPendingWork() below
handler.removeMessages(QueuedWorkHandler.MSG_RUN);
if (DEBUG) {
hadMessages = true;
Log.d(LOG_TAG, "waiting");
}
}
// We should not delay any work as this might delay the finishers
sCanDelay = false;
}
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
//鎵ц鎵€鏈夌殑纾佺洏鍐欏叆浠诲姟
processPendingWork();
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
try {
//鎵ц鎵€鏈夌殑 postWriteRunnable
while (true) {
Runnable finisher;
synchronized (sLock) {
finisher = sFinishers.poll();
}
if (finisher == null) {
break;
}
finisher.run();
}
} finally {
sCanDelay = true;
}
synchronized (sLock) {
long waitTime = System.currentTimeMillis() - startTime;
if (waitTime > 0 || hadMessages) {
mWaitTimes.add(Long.valueOf(waitTime).intValue());
mNumWaits++;
if (DEBUG || mNumWaits % 1024 == 0 || waitTime > MAX_WAIT_TIME_MILLIS) {
mWaitTimes.log(LOG_TAG, "waited: ");
}
}
}
}
ActivityThread 涓轰粈涔堣涓诲姩鍘昏Е鍙戞墽琛屾墍鏈夌殑纾佺洏鍐欏叆浠诲姟鎴戞棤浠庡緱鐭ワ紝瀛楄妭鎶€鏈烦鍔ㄥ洟闃熺粰鍑虹殑鐚滄祴鏄細Google 鍦?Activity 鍜?Service 璋冪敤 onStop 涔嬪墠闃诲涓荤嚎绋嬫潵澶勭悊 SP锛屾垜浠兘鐚滃埌鐨勫敮涓€鍘熷洜鏄敖鍙兘鐨勪繚璇佹暟鎹殑鎸佷箙鍖栥€傚洜涓哄鏋滃湪杩愯杩囩▼涓骇鐢熶簡 crash锛屼篃浼氬鑷?SP 鏈寔涔呭寲锛屾寔涔呭寲鏈韩鏄?IO 鎿嶄綔锛屼篃浼氬け璐?/strong>
缁间笂鎵€杩帮紝鐢变簬 SP 鏈韩鍙敮鎸佸叏閲忔洿鏂帮紝濡傛灉 SP 鏂囦欢寰堝ぇ锛屽嵆浣挎槸灏忔暟鎹噺鐨?apply/commit 鎿嶄綔涔熸湁鍙兘瀵艰嚧 ANR
姝e弽闈?/h2>
SharedPreferencesImpl 鍦ㄤ笉鍚岀殑绯荤粺鐗堟湰涓湁鐫€姣旇緝澶х殑宸埆锛屼緥濡?writeToFile 鏂规硶瀵逛簬浠诲姟鐗堟湰鍙风殑鏍¢獙涔熸槸浠?8.0 绯荤粺寮€濮嬬殑锛屽湪 8.0 绯荤粺涔嬪墠瀵逛簬杩炵画鐨?commit 鍜?apply 姣忔閮戒細瑙﹀彂 I/O 鎿嶄綔锛屾墍浠ュ湪 8.0 绯荤粺涔嬪墠 ANR 闂浼氭洿鍔犲鏄撳鐜般€傛垜浠渶瑕佹牴鎹郴缁熺増鏈潵鐪嬪緟浠ヤ笂鍒椾妇鍑烘潵鐨勫悇涓己闄?/p>
闇€瑕佸己璋冪殑鏄紝SP 鏈韩鐨勫畾浣嶆槸杞婚噺绾ф暟鎹瓨鍌紝璁捐鍒濊》鏄敤浜庡瓨鍌ㄧ畝鍗曠殑鏁版嵁缁撴瀯锛堝熀鏈暟鎹被鍨嬶級锛屼笖鎻愪緵浜嗘寜妯″潡鍒嗗尯瀛樺偍鐨勫姛鑳姐€傚鏋滃紑鍙戣€呰兘澶熶弗鏍奸伒瀹堣繖涓€涓鑼冪殑璇濓紝閭d箞鍏跺疄浠ヤ笂鎵€杩扮殑寰堝鈥滅己闄封€濋兘鏄彲浠ラ伩鍏嶇殑銆傝€?SP 涔嬫墍浠ョ幇鍦ㄧ湅璧锋潵闂寰堝锛屼篃鏄洜涓哄浠婂ぇ閮ㄥ垎搴旂敤鐨勪笟鍔℃瘮浠ュ墠澶嶆潅澶浜嗭紝鏈変簺鏃跺€欎负浜嗘柟渚垮氨鐩存帴鐢ㄦ潵瀛樺偍闈炲父澶嶆潅鐨勬暟鎹粨鏋勶紝鎴栬€呮槸娌℃湁鍋氬ソ鏁版嵁鍒嗗尯瀛樺偍锛屽鑷村崟涓枃浠惰繃澶э紝杩欐墠鏄€犳垚闂鐨勪富瑕佸師鍥?/p>
濡備綍鍋氬ソ鎸佷箙鍖?/h2>
浠ヤ笅鐨勭ず渚嬩唬鐮佷及璁℃槸寰堝寮€鍙戣€呯殑鍣╂ⅵ
val sharedPreference = getSharedPreferences("user_preference", Context.MODE_PRIVATE)
val name = sharedPreference.getString("name", "")
浠ヤ笂浠g爜瀛樺湪浠€涔堥棶棰樺憿锛熸垜瑙夊緱鑷冲皯鏈変簲鐐癸細
- 寮哄紩鐢ㄥ埌浜?SP锛屽鑷村悗缁渶瑕佸垏鎹㈠瓨鍌ㄥ簱鏃堕渶瑕佸叏灞€鎼滅储鏇挎崲锛屽伐浣滈噺闈炲父澶?/li>
- key 鍊奸毦缁存姢锛屾瘡娆¤幏鍙?value 鏃堕兘闇€瑕佹樉寮忓0鏄?key 鍊?/li>
- 鍙鎬у樊锛岄敭鍊煎鐨勫惈涔夊熀鏈彧鑳介潬 key 鍊艰繘琛岃〃绀?/li>
- 鍙敮鎸佸熀鏈暟鎹被鍨嬶紝鍦ㄥ瓨鍙栬嚜瀹氫箟鏁版嵁绫诲瀷鏃跺瓨鍦ㄥ緢澶氶噸澶嶅伐浣溿€傝鍚?SP 瀛樺叆鑷畾涔夌殑 - JavaBean 瀵硅薄鏃讹紝鍙兘灏?Bean 瀵硅薄杞负 Json 瀛楃涓插悗瀛樺叆 SP锛屽湪鍙栧€兼椂鍐嶆墜鍔ㄥ弽搴忓垪鍖?/li>
- 鏁版嵁绫诲瀷涓嶆槑纭紝鍩烘湰鍙兘闈犳敞閲婃潵寮曞寮€鍙戣€呬娇鐢ㄦ纭殑鏁版嵁绫诲瀷
寮€鍙戣€呭線寰€鏄細澹版槑鍑哄悇绉?SpUtils 绫昏繘琛屽涓€灞傚皝瑁咃紝浣嗕篃娌℃硶褰诲簳瑙e喅浠ヤ笂闂銆係P 鐨勭‘鏄瓨鍦ㄧ潃涓€浜涜璁$己闄凤紝浣嗗浜庡ぇ閮ㄥ垎搴旂敤寮€鍙戣€呮潵璇村叾瀹炲苟娌℃湁澶氬皯閫夋嫨锛屾垜浠彧鑳介€夋嫨鐢ㄦ垨鑰呬笉鐢紝骞舵病鏈夊灏戜綑鍦板彲浠ユ潵瑙e喅鎴栬€呴伩鍏嶅叾瀛樺湪鐨勯棶棰橈紝鎴戜滑寰€寰€鍙兘鍦ㄩ亣鍒伴棶棰樺悗鍒囨崲鍒板叾瀹冪殑鎸佷箙鍖栧瓨鍌ㄦ柟妗?br> 鐩墠鏈変袱涓瘮杈冪煡鍚嶇殑鎸佷箙鍖栧瓨鍌ㄦ柟妗堬細Jetpack DataStore 鍜岃吘璁殑 MMKV锛屾垜浠綋鐒跺彲浠ラ€夋嫨灏嗛」鐩腑鐨?SP 鍒囨崲涓鸿繖涓や釜搴撲箣涓€锛屼絾杩欎篃涓嶇璁╀汉鎯冲埌涓€涓棶棰橈紝濡傛灉浠ュ悗杩欎袱涓簱涔熼亣鍒颁簡闂鐢氳嚦鏄洿鎺ヨ搴熷純浜嗭紝闅鹃亾鎴戜滑鍙堥渶瑕佸啀鏉ュ叏灞€鏇挎崲涓€閬嶅悧锛熸垜浠簲璇ュ浣曡璁℃墠鑳戒娇寰楁瘡娆$殑鏇挎崲鎴愭湰闄嶅埌鏈€浣庡憿锛?br> 鍦ㄦ垜鐪嬫潵锛屽紑鍙戣€呭湪涓洪」鐩紩鍏ヤ竴涓柊鐨勪緷璧栧簱涔嬪墠灏卞簲璇ヤ负浠ュ悗绉婚櫎璇ュ簱鍋氬ソ鍑嗗锛屽仛濂芥帴鍙i殧绂伙紝灞忚斀鍏蜂綋鐨勫簳灞傞€昏緫锛堝綋鐒讹紝涔熶笉鏄瘡涓緷璧栧簱閮藉彲浠ュ仛鍒帮級銆傜瑪鑰呯殑椤圭洰涔嬪墠涔熸槸浣跨敤 SP 鏉ュ瓨鍌ㄩ厤缃俊鎭紝鍚庢潵鎴戜篃灏嗗叾鍒囨崲鍒颁簡 MMKV锛屼笅闈㈠氨鏉ヤ粙缁嶄笅绗旇€呭綋鏃舵槸濡備綍璁捐瀛樺偍缁撴瀯閬垮厤纭紪鐮佺殑
鐩墠鐨勬晥鏋?/p>
鎴戝皢搴旂敤鍐呮墍鏈夐渶瑕佸瓨鍌ㄧ殑閿€煎鏁版嵁鍒嗕负浜嗕笁绫伙細鐢ㄦ埛寮哄叧鑱旀暟鎹€佸簲鐢ㄩ厤缃暟鎹€佷笉鍙簩娆″彉鏇寸殑鏁版嵁銆傛瘡涓€绫绘暟鎹殑瀛樺偍鍖哄煙鍚勪笉鐩稿悓锛屼簰涓嶅奖鍝嶃€傝繘琛屾暟鎹垎缁勭殑濂藉灏卞湪浜庡彲浠ユ牴鎹渶瑕佹潵娓呴櫎鐗瑰畾鏁版嵁锛屼緥濡傚綋鐢ㄦ埛閫€鐧诲悗鎴戜滑鍙互鍙竻闄?UserKVHolder锛岃€?PreferenceKVHolder 鍜?FinalKVHolder 鍒欏彲浠ヤ竴鐩翠繚鐣?br> IKVHolder 鎺ュ彛瀹氫箟浜嗗熀鏈殑瀛樺彇鏂规硶锛孧MKVKVHolder 閫氳繃 MMKV 瀹炵幇浜嗗叿浣撶殑瀛樺偍閫昏緫
//鍜岀敤鎴峰己缁戝畾鐨勬暟鎹紝鍦ㄩ€€鍑虹櫥褰曟椂闇€瑕佸叏閮ㄦ竻闄わ紝渚嬪 UserBean
//璁剧疆 encryptKey 浠ヤ究鍔犲瘑瀛樺偍
private val UserKVHolder: IKVHolder = MMKVKVHolder("user", "鍔犲瘑key")
//鍜岀敤鎴蜂笉寮哄叧鑱旂殑鏁版嵁锛屽湪閫€鍑虹櫥褰曟椂鏃犻渶娓呴櫎锛屼緥濡傚闂存ā寮忋€佸瓧浣撳ぇ灏忕瓑
private val PreferenceKVHolder: IKVHolder = MMKVKVHolder("preference")
//鐢ㄤ簬瀛樺偍涓嶄細浜屾鍙樻洿鍙敤浜庡巻鍙叉函婧愮殑鏁版嵁锛屼緥濡傚簲鐢ㄩ娆″畨瑁呯殑鏃堕棿銆佺増鏈彿銆佺増鏈悕绛?
private val FinalKVHolder: IKVHolder = MMKVKVFinalHolder("final")
涔嬪悗鎴戜滑灏卞彲浠ュ埄鐢?Kotlin 寮哄ぇ鐨勮娉曠壒鎬ф潵瀹氫箟閿€煎浜?br> 渚嬪锛屽浜庡拰鐢ㄦ埛寮哄叧鑱旂殑鏁版嵁锛屾瘡涓敭鍊煎閮藉畾涔変负 UserKV 鐨勪竴涓睘鎬у瓧娈碉紝閿€煎鐨勫惈涔夊拰浣滅敤閫氳繃灞炴€у悕鏉ヨ繘琛屾爣璇嗭紝涓旈敭鍊煎鐨?key 蹇呴』鍜屽睘鎬у悕淇濇寔涓€鑷达紝杩欐牱鍙互閬垮厤 key 鍊奸噸澶嶃€傛瘡涓?getValue 鎿嶄綔涔熼兘鏀寔璁剧疆榛樿鍊笺€侷KVHolder 鍐呴儴閫氳繃 Gson 鏉ュ疄鐜板簭鍒楀寲鍜屽弽搴忓垪鍖栵紝杩欐牱 UserKV 灏卞彲浠ョ洿鎺ュ瓨鍌?JavaBean銆丣avaBeanList锛孧ap 绛夋暟鎹粨鏋勪簡
object UserKV : IKVHolder by UserKVHolder {
var name: String
get() = get("name", "")
set(value) = set("name", value)
var blog: String
get() = get("blog", "")
set(value) = set("blog", value)
var userBean: UserBean?
get() = getBeanOrNull("userBean")
set(value) = set("userBean", value)
var userBeanOfDefault: UserBean
get() = getBeanOrDefault(
"userBeanOfDefault",
UserBean("涓氬織闄?, "https://juejin.cn/user/923245496518439")
)
set(value) = set("userBeanOfDefault", value)
var userBeanList: List<UserBean>
get() = getBean("userBeanList")
set(value) = set("userBeanList", value)
var map: Map<Int, String>
get() = getBean("map")
set(value) = set("map", value)
}
姝ゅ锛屾垜浠篃鍙互鍦?setValue 鏂规硶涓 value 杩涜鏍¢獙锛岄伩鍏嶆棤鏁堝€?/p>
object UserKV : IKVHolder by UserKVHolder {
var age: Int
get() = get("age", 0)
set(value) {
if (value <= 0) {
return
}
set("age", value)
}
}
涔嬪悗鎴戜滑鍦ㄥ瓨鍙栧€兼椂锛屽氨鐩稿綋浜庡湪鐩存帴璇诲啓 UserKV 鐨勫睘鎬у€硷紝涔熸敮鎸佸姩鎬佹寚瀹?Key 杩涜璧嬪€煎彇鍊硷紝鍦ㄦ槗鐢ㄦ€у拰鍙鎬т笂鐩告瘮 SharedPreferences 閮芥湁寰堝ぇ鐨勬彁鍗囷紝涓斿浜庡閮ㄦ潵璇村畬鍏ㄥ睆钄戒簡鍏蜂綋鐨勫瓨鍌ㄥ疄鐜伴€昏緫
//瀛樺€?
UserKV.name = "xxxxx"
UserKV.blog = "https://juejin.cn/user/923245496518439"
//鍙栧€?
val name = UserKV.name
val blog = UserKV.blog
//鍔ㄦ€佹寚瀹?Key 杩涜璧嬪€煎拰鍙栧€?
UserKV.set("name", "xxx")
val name = UserKV.get("name", "")
濡備綍璁捐鐨?/p>
棣栧厛锛孖KVHolder 瀹氫箟浜嗗熀鏈殑瀛樺彇鏂规硶锛岄櫎浜嗛渶瑕佹敮鎸佸熀鏈暟鎹被鍨嬪锛岃繕闇€瑕佹敮鎸佽嚜瀹氫箟鐨勬暟鎹被鍨嬨€備緷闈?Kotlin 鐨?鎵╁睍鍑芥暟 鍜?鍐呰仈鍑芥暟 杩欎袱涓娉曠壒鎬э紝鎴戜滑鍦ㄥ瓨鍙栬嚜瀹氫箟绫诲瀷鏃堕兘鏃犻渶澹版槑娉涘瀷绫诲瀷锛屼娇鐢ㄤ笂鍗佸垎绠€娲併€侸sonHolder 鍒欐槸閫氳繃 Gson 瀹炵幇浜嗗熀鏈殑搴忓垪鍖栧拰鍙嶅簭鍒楀寲鏂规硶
interface IKVHolder {
companion object {
inline fun <reified T> IKVHolder.getBean(key: String): T {
return JsonHolder.toBean(get(key, ""))
}
inline fun <reified T> IKVHolder.getBeanOrNull(key: String): T{
return JsonHolder.toBeanOrNull(get(key, ""))
}
inline fun <reified T> IKVHolder.getBeanOrDefault(key: String, defaultValue: T): T {
return JsonHolder.toBeanOrDefault(get(key, ""), defaultValue)
}
fun toJson(ob: Any?): String {
return JsonHolder.toJson(ob)
}
}
//鏁版嵁鍒嗙粍锛岀敤浜庢爣鏄庝笉鍚岃寖鍥村唴鐨勬暟鎹紦瀛?
val keyGroup: String
fun verifyBeforePut(key: String, value: Any?): Boolean
fun get(key: String, default: Int): Int
fun set(key: String, value: Int)
fun <T> set(key: String, value: T?)
fun containsKey(key: String): Boolean
fun removeKey(vararg keys: String)
fun allKeyValue(): Map<String, Any?>
fun clear()
路路路
}
BaseMMKVKVHolder 瀹炵幇浜?IKVHolder 鎺ュ彛锛屽唴閮ㄥ紩鍏ヤ簡 MMKV 浣滀负鍏蜂綋鐨勬寔涔呭寲瀛樺偍鏂规
/**
* @param selfGroup 鐢ㄤ簬鎸囧畾鏁版嵁鍒嗙粍锛屼笉鍚屽垎缁勪笅鐨勬暟鎹簰涓嶅叧鑱?
* @param encryptKey 鍔犲瘑 key锛屽鏋滀负绌哄垯琛ㄧず涓嶈繘琛屽姞瀵?
*/
sealed class BaseMMKVKVHolder constructor(
selfGroup: String,
encryptKey: String
) : IKVHolder {
final override val keyGroup: String = selfGroup
override fun verifyBeforePut(key: String, value: Any?): Boolean {
return true
}
private val kv: MMKV= if (encryptKey.isBlank()) MMKV.mmkvWithID(
keyGroup,
MMKV.MULTI_PROCESS_MODE
) else MMKV.mmkvWithID(keyGroup, MMKV.MULTI_PROCESS_MODE, encryptKey)
override fun set(key: String, value: Int) {
if (verifyBeforePut(key, value)) {
kv?.putInt(key, value)
}
}
override fun <T> set(key: String, value: T?) {
if (verifyBeforePut(key, value)) {
if (value == null) {
removeKey(key)
} else {
set(key, toJson(value))
}
}
}
override fun get(key: String, default: Int): Int {
return kv?.getInt(key, default) ?: default
}
override fun containsKey(key: String): Boolean {
return kv?.containsKey(key) ?: false
}
override fun removeKey(vararg keys: String) {
kv?.removeValuesForKeys(keys)
}
override fun allKeyValue(): Map<String, Any?> {
val map = mutableMapOf<String, Any?>()
kv?.allKeys()?.forEach {
map[it] = getObjectValue(kv, it)
}
return map
}
override fun clear() {
kv?.clearAll()
}
路路路
}
BaseMMKVKVHolder 鏈変袱涓瓙绫伙紝鍏跺尯鍒彧鍦ㄤ簬 MMKVKVFinalHolder 淇濆瓨閿€煎鍚庢棤娉曞啀娆℃洿鏀瑰€硷紝鐢ㄤ簬瀛樺偍涓嶄細浜屾鍙樻洿鍙敤浜庡巻鍙叉函婧愮殑鏁版嵁锛屼緥濡傚簲鐢ㄩ娆″畨瑁呮椂鐨勬椂闂存埑銆佺増鏈彿銆佺増鏈悕绛?/p>
/**
* @param selfGroup 鐢ㄤ簬鎸囧畾鏁版嵁鍒嗙粍锛屼笉鍚屽垎缁勪笅鐨勬暟鎹簰涓嶅叧鑱?
* @param encryptKey 鍔犲瘑 key锛屽鏋滀负绌哄垯琛ㄧず涓嶈繘琛屽姞瀵?
*/
class MMKVKVHolder constructor(selfGroup: String, encryptKey: String = "") :
BaseMMKVKVHolder(selfGroup, encryptKey)
/**
* 瀛樺偍鍚庡€兼棤娉曚簩娆″彉鏇?
* @param selfGroup 鐢ㄤ簬鎸囧畾鏁版嵁鍒嗙粍锛屼笉鍚屽垎缁勪笅鐨勬暟鎹簰涓嶅叧鑱?
* @param encryptKey 鍔犲瘑 key锛屽鏋滀负绌哄垯琛ㄧず涓嶈繘琛屽姞瀵?
*/
class MMKVKVFinalHolder constructor(selfGroup: String, encryptKey: String = "") :
BaseMMKVKVHolder(selfGroup, encryptKey) {
override fun verifyBeforePut(key: String, value: Any?): Boolean {
return !containsKey(key)
}
}
閫氳繃鎺ュ彛闅旂锛孶serKV 灏卞畬鍏ㄤ笉浼氭帴瑙﹀埌鍏蜂綋鐨勫瓨鍌ㄥ疄鐜版満鍒朵簡锛屽浜庡紑鍙戣€呮潵璇翠篃鍙槸鍦ㄨ鍐?UserKV 鐨勪竴涓睘鎬у瓧娈佃€屽凡锛屽綋鍚庣画鎴戜滑闇€瑕佹浛鎹㈠瓨鍌ㄦ柟妗堟椂锛屼篃鍙渶瑕佸幓鏀瑰姩 MMKVKVHolder 鐨勫唴閮ㄥ疄鐜板嵆鍙紝涓婂眰搴旂敤瀹屽叏涓嶉渶瑕佽繘琛屼换浣曟敼鍔?/p>
KVHolder
KVHolder 鐨勫疄鐜版€濊矾杩樻槸鍗佸垎绠€鍗曠殑锛屽啀鍔犱笂 Kotlin 鏈韩寮哄ぇ鐨勮娉曠壒鎬у氨杩涗竴姝ユ彁楂樹簡鏄撶敤鎬у拰鍙鎬?馃槆馃槆 鎴戜篃灏嗗叾鍙戝竷涓哄紑婧愬簱锛屾劅鍏磋叮鐨勮鑰呭彲浠ョ洿鎺ヨ繙绋嬪鍏ヤ緷璧?/p>
allprojects {
repositories {
maven { url "https://jitpack.io" }
}
}
dependencies {
implementation 'com.github.leavesC:KVHolder:latest_version'
}
GitHub 鐐瑰嚮杩欓噷锛欿VHolder