当前位置: 首页>前端>正文

SharedPreferences 的缺陷及一点点思考

[杞琞涓€鏂囪鎳?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>

  1. writeToDiskRunnable 浣跨敤鍒颁簡鍐呴儴閿?mWritingToDiskLock 鏉ヤ繚璇?writeToFile 鎿嶄綔鐨勬湁搴忔€э紝閬垮厤澶氱嚎绋嬬珵浜?/li>
  2. 瀵逛簬 commit 鎿嶄綔锛屽鏋滃綋鍓嶅彧鏈変竴涓嚎绋嬪湪鎵ц鎻愪氦淇敼鐨勬搷浣滅殑璇濓紝閭d箞鐩存帴鍦ㄨ绾跨▼涓婃墽琛?writeToDiskRunnable锛屾祦绋嬬粨鏉?/li>
  3. 瀵逛簬鍏朵粬鎯呭喌锛坅pply 鎿嶄綔銆佸绾跨▼鍚屾椂 commit 鎴栬€?apply锛夛紝閮戒細灏?writeToDiskRunnable 鎻愪氦缁?QueuedWork 鎵ц
  4. 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


https://www.xamrdz.com/web/2ua1995639.html

相关文章: