精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Android源碼進(jìn)階之深入理解SharedPreference原理機(jī)制

移動開發(fā) Android
SharedPreferences的本身實(shí)現(xiàn)就是分為兩步,一步是內(nèi)存,一部是磁盤,而主線程又依賴SharedPreferences的寫入,所以可能當(dāng)io成為瓶頸的時候,App會因?yàn)镾haredPreferences變的卡。

[[429060]]

前言

很久沒有分析源碼了,今天我們來分析下SharedPreferences;

大家一起來學(xué)習(xí);

一、SharedPreferences簡單使用

1、創(chuàng)建

第一個參數(shù)是儲存的xml文件名稱,第二個是打開方式,一般就用

  1. Context.MODE_PRIVATE; 
  2. SharedPreferences sp=context.getSharedPreferences("名稱", Context.MODE_PRIVATE); 

2、寫入

  1. //可以創(chuàng)建一個新的SharedPreference來對儲存的文件進(jìn)行操作 
  2. SharedPreferences sp=context.getSharedPreferences("名稱", Context.MODE_PRIVATE); 
  3. //像SharedPreference中寫入數(shù)據(jù)需要使用Editor 
  4. SharedPreference.Editor editor = sp.edit(); 
  5. //類似鍵值對 
  6. editor.putString("name""string"); 
  7. editor.putInt("age", 0); 
  8. editor.putBoolean("read"true); 
  9. //editor.apply(); 
  10. editor.commit(); 
  • apply和commit都是提交保存,區(qū)別在于apply是異步執(zhí)行的,不需要等待。不論刪除,修改,增加都必須調(diào)用apply或者commit提交保存;
  • 關(guān)于更新:如果已經(jīng)插入的key已經(jīng)存在。那么將更新原來的key;
  • 應(yīng)用程序一旦卸載,SharedPreference也會被刪除;

3、讀取

  1. SharedPreference sp=context.getSharedPreferences("名稱", Context.MODE_PRIVATE); 
  2. //第一個參數(shù)是鍵名,第二個是默認(rèn)值 
  3. String name=sp.getString("name""暫無"); 
  4. int age=sp.getInt("age", 0); 
  5. boolean read=sp.getBoolean("isRead"false); 

4、檢索

  1. SharedPreferences sp=context.getSharedPreferences("名稱", Context.MODE_PRIVATE); 
  2. //檢查當(dāng)前鍵是否存在 
  3. boolean isContains=sp.contains("key"); 
  4. //使用getAll可以返回所有可用的鍵值 
  5. //Map<String,?> allMaps=sp.getAll(); 

5、刪除

當(dāng)我們要清除SharedPreferences中的數(shù)據(jù)的時候一定要先clear()、再commit(),不能直接刪除xml文件;

  1. SharedPreference sp=getSharedPreferences("名稱", Context.MODE_PRIVATE); 
  2. SharedPrefence.Editor editor=sp.edit(); 
  3. editor.clear(); 
  4. editor.commit(); 
  • getSharedPreference() 不會生成文件,這個大家都知道;
  • 刪除掉文件后,再次執(zhí)行commit(),刪除的文件會重生,重生文件的數(shù)據(jù)和刪除之前的數(shù)據(jù)相同;
  • 刪除掉文件后,程序在沒有完全退出停止運(yùn)行的情況下,Preferences對象所存儲的內(nèi)容是不變的,雖然文件沒有了,但數(shù)據(jù)依然存在;程序完全退出停止之后,數(shù)據(jù)才會丟失;
  • 清除SharedPreferences數(shù)據(jù)一定要執(zhí)行editor.clear(),editor.commit(),不能只是簡單的刪除文件,這也就是最后的結(jié)論,需要注意的地方

二、SharedPreferences源碼分析

1、創(chuàng)建

  1. SharedPreferences preferences = getSharedPreferences("test", Context.MODE_PRIVATE); 

實(shí)際上context的真正實(shí)現(xiàn)類是ContextImp,所以進(jìn)入到ContextImp的getSharedPreferences方法查看:

  1. @Override 
  2.    public SharedPreferences getSharedPreferences(String nameint mode) { 
  3.        ...... 
  4.        File file; 
  5.        synchronized (ContextImpl.class) { 
  6.            if (mSharedPrefsPaths == null) { 
  7.            //定義類型:ArrayMap<String, File> mSharedPrefsPaths; 
  8.                mSharedPrefsPaths = new ArrayMap<>(); 
  9.            } 
  10.            //從mSharedPrefsPaths中是否能夠得到file文件 
  11.            file = mSharedPrefsPaths.get(name); 
  12.            if (file == null) {//如果文件為null 
  13.            //就創(chuàng)建file文件 
  14.                file = getSharedPreferencesPath(name); 
  15.                將name,file鍵值對存入集合中 
  16.                mSharedPrefsPaths.put(name, file); 
  17.            } 
  18.        } 
  19.        return getSharedPreferences(file, mode); 
  20.    } 

ArrayMap<String, File> mSharedPrefsPaths;對象是用來存儲SharedPreference文件名稱和對應(yīng)的路徑,獲取路徑是在下列方法中,就是獲取data/data/包名/shared_prefs/目錄下的

  1. @Override 
  2. public File getSharedPreferencesPath(String name) { 
  3.     return makeFilename(getPreferencesDir(), name + ".xml"); 
  4. private File getPreferencesDir() { 
  5.         synchronized (mSync) { 
  6.             if (mPreferencesDir == null) { 
  7.                 mPreferencesDir = new File(getDataDir(), "shared_prefs"); 
  8.             } 
  9.             return ensurePrivateDirExists(mPreferencesDir); 
  10.         } 

路徑之后才開始創(chuàng)建對象

  1. @Override 
  2.   public SharedPreferences getSharedPreferences(File file, int mode) { 
  3.   //重點(diǎn)1 
  4.       checkMode(mode); 
  5.   ....... 
  6.       SharedPreferencesImpl sp; 
  7.       synchronized (ContextImpl.class) { 
  8.       //獲取緩存對象(或者創(chuàng)建緩存對象) 
  9.           final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked(); 
  10.           //通過鍵file從緩存對象中獲取Sp對象 
  11.           sp = cache.get(file); 
  12.           //如果是null,就說明緩存中還沒后該文件的sp對象 
  13.           if (sp == null) { 
  14.           //重點(diǎn)2:從磁盤讀取文件 
  15.               sp = new SharedPreferencesImpl(file, mode); 
  16.               //添加到內(nèi)存中 
  17.               cache.put(file, sp); 
  18.               //返回sp 
  19.               return sp; 
  20.           } 
  21.       } 
  22.       //如果設(shè)置為MODE_MULTI_PROCESS模式,那么將執(zhí)行SP的startReloadIfChangedUnexpectedly方法。 
  23.       if ((mode & Context.MODE_MULTI_PROCESS) != 0 || 
  24.           getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) { 
  25.           sp.startReloadIfChangedUnexpectedly(); 
  26.       } 
  27.       return sp; 
  28.   } 

就是重載之前的方法,只是入?yún)⒂晌募臑镕ile了,給創(chuàng)建過程加鎖了synchronized ,通過方法getSharedPreferencesCacheLocked()獲取系統(tǒng)中存儲的所有包名以及對應(yīng)的文件,這就是每個sp文件只有一個對應(yīng)的SharedPreferencesImpl實(shí)現(xiàn)對象原因

流程:

  • 獲取緩存區(qū),從緩存區(qū)中獲取數(shù)據(jù),看是否存在sp對象,如果存在就直接返回
  • 如果不存在,那么就從磁盤獲取數(shù)據(jù),
  • 從磁盤獲取的數(shù)據(jù)之后,添加到內(nèi)存中,
  • 返回sp;

getSharedPreferencesCacheLocked

  1. private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() { 
  2.         if (sSharedPrefsCache == null) { 
  3.             sSharedPrefsCache = new ArrayMap<>(); 
  4.         } 
  5.         final String packageName = getPackageName(); 
  6.         ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefsCache.get(packageName); 
  7.         if (packagePrefs == null) { 
  8.             packagePrefs = new ArrayMap<>(); 
  9.             sSharedPrefsCache.put(packageName, packagePrefs); 
  10.         } 
  11.         return packagePrefs; 
  12.     } 
  • getSharedPreferences(File file, int mode)方法中,從上面的系統(tǒng)緩存中分局File獲取SharedPreferencesImpl對象,如果之前沒有使用過,就需要創(chuàng)建一個對象了,通過方法checkMode(mode);
  • 先檢查mode是否是三種模式,然后通過sp = new SharedPreferencesImpl(file, mode);
  • 創(chuàng)建對象,并將創(chuàng)建的對象放到系統(tǒng)的packagePrefs中,方便以后直接獲取;
  1. SharedPreferencesImpl(File file, int mode) { 
  2.         mFile = file; //存儲文件 
  3.         //備份文件(災(zāi)備文件) 
  4.         mBackupFile = makeBackupFile(file); 
  5.         //模式 
  6.         mMode = mode; 
  7.         //是否加載過了 
  8.         mLoaded = false
  9.         // 存儲文件內(nèi)的鍵值對信息 
  10.         mMap = null
  11.         //從名字可以知道是:開始加載數(shù)據(jù)從磁盤 
  12.         startLoadFromDisk(); 
  13.     } 
  • 主要是設(shè)置了幾個參數(shù),mFile 是原始文件;mBackupFile 是后綴.bak的備份文件;
  • mLoaded標(biāo)識是否正在加載修改文件;
  • mMap用來存儲sp文件中的數(shù)據(jù),存儲時候也是鍵值對形式,獲取時候也是通過這個獲取,這就是表示每次使用sp的時候,都是將數(shù)據(jù)寫入內(nèi)存,也就是sp數(shù)據(jù)存儲數(shù)據(jù)快的原因,所以sp文件不能存儲大量數(shù)據(jù),否則執(zhí)行時候很容易會導(dǎo)致OOM;
  • mThrowable加載文件時候報的錯誤;
  • 下面就是加載數(shù)據(jù)的方法startLoadFromDisk();從sp文件中加載數(shù)據(jù)到mMap中

2、startLoadFromDisk()

  1. private void startLoadFromDisk() { 
  2.        synchronized (mLock) { 
  3.            mLoaded = false
  4.        } 
  5.        //開啟子線程加載磁盤數(shù)據(jù) 
  6.        new Thread("SharedPreferencesImpl-load") { 
  7.            public void run() { 
  8.                loadFromDisk(); 
  9.            } 
  10.        }.start(); 
  11.    } 
  12.    private void loadFromDisk() { 
  13.        synchronized (mLock) { 
  14.        //如果加載過了 直接返回 
  15.            if (mLoaded) { 
  16.                return
  17.            } 
  18.            //備份文件是否存在, 
  19.            if (mBackupFile.exists()) { 
  20.            //刪除file原文件 
  21.                mFile.delete(); 
  22.                //將備份文件命名為:xml文件 
  23.                mBackupFile.renameTo(mFile); 
  24.            } 
  25.        } 
  26.        ....... 
  27.        Map map = null
  28.        StructStat stat = null
  29.        try { 
  30.        //下面的就是讀取數(shù)據(jù) 
  31.            stat = Os.stat(mFile.getPath()); 
  32.            if (mFile.canRead()) { 
  33.                BufferedInputStream str = null
  34.                try { 
  35.                    str = new BufferedInputStream( 
  36.                            new FileInputStream(mFile), 16*1024); 
  37.                    map = XmlUtils.readMapXml(str); 
  38.                } catch (Exception e) { 
  39.                    Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e); 
  40.                } finally { 
  41.                    IoUtils.closeQuietly(str); 
  42.                } 
  43.            } 
  44.        } catch (ErrnoException e) { 
  45.            /* ignore */ 
  46.        } 
  47.        synchronized (mLock) { 
  48.        //已經(jīng)加載完畢, 
  49.            mLoaded = true
  50.            //數(shù)據(jù)不是null 
  51.            if (map != null) { 
  52.            //將map賦值給全局的存儲文件鍵值對的mMap對象 
  53.                mMap = map; 
  54.                //更新內(nèi)存的修改時間以及文件大小 
  55.                mStatTimestamp = stat.st_mtime; 
  56.                mStatSize = stat.st_size; 
  57.            } else { 
  58.                mMap = new HashMap<>(); 
  59.            } 
  60.            //重點(diǎn):喚醒所有以mLock鎖的等待線程 
  61.            mLock.notifyAll(); 
  62.        } 
  63.    } 
  • 首先判斷備份文件是否存在,如果存在,就更該備份文件的后綴名;接著就開始讀取數(shù)據(jù),然后將讀取的數(shù)據(jù)賦值給全局變量存儲文件鍵值對的mMap對象,并且更新修改時間以及文件大小變量;
  • 喚醒所有以mLock為鎖的等待線程;
  • 到此為止,初始化SP對象就算完成了,其實(shí)可以看出來就是一個二級緩存流程:磁盤到內(nèi)存;

3、get獲取SP中的鍵值對

  1. @Nullable 
  2.    public String getString(String key, @Nullable String defValue) { 
  3.        synchronized (mLock) { 鎖判斷 
  4.            awaitLoadedLocked(); //等待機(jī)制 
  5.            String v = (String)mMap.get(key); //從鍵值對中獲取數(shù)據(jù) 
  6.            return v != null ? v : defValue; 
  7.        } 
  8.    } 
  9. private void awaitLoadedLocked() { 
  10.        ....... 
  11.        while (!mLoaded) { //在加載數(shù)據(jù)完畢的時候,值為true 
  12.            try { 
  13.            //線程等待 
  14.                mLock.wait(); 
  15.            } catch (InterruptedException unused) { 
  16.            } 
  17.        } 
  18.    } 

如果數(shù)據(jù)沒有加載完畢(也就是說mLoaded=false),此時將線程等待;

4、putXXX以及apply源碼

  1. public Editor edit() { 
  2.         //跟getXXX原理一樣 
  3.         synchronized (mLock) { 
  4.             awaitLoadedLocked(); 
  5.         } 
  6.         //返回EditorImp對象 
  7.         return new EditorImpl(); 
  8.     } 
  9.  public Editor putBoolean(String key, boolean value) { 
  10.       synchronized (mLock) { 
  11.            mModified.put(key, value); 
  12.            return this; 
  13.          } 
  14.  } 
  15.        public void apply() { 
  16.             final long startTime = System.currentTimeMillis(); 
  17.             //根據(jù)名字可以知道:提交數(shù)據(jù)到內(nèi)存 
  18.             final MemoryCommitResult mcr = commitToMemory(); 
  19.            ........ 
  20. //提交數(shù)據(jù)到磁盤中 
  21.             SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable); 
  22.             //重點(diǎn):調(diào)用listener 
  23.             notifyListeners(mcr); 
  24.         } 
  • 先執(zhí)行了commitToMemory,提交數(shù)據(jù)到內(nèi)存;然后提交數(shù)據(jù)到磁盤中;
  • 緊接著調(diào)用了listener;

5、commitToMemory

  1.      private MemoryCommitResult commitToMemory() { 
  2.             long memoryStateGeneration; 
  3.             List<String> keysModified = null
  4.             Set<OnSharedPreferenceChangeListener> listeners = null
  5.             //寫到磁盤的數(shù)據(jù)集合 
  6.             Map<String, Object> mapToWriteToDisk; 
  7.             synchronized (SharedPreferencesImpl.this.mLock) { 
  8.                 if (mDiskWritesInFlight > 0) { 
  9.                     mMap = new HashMap<String, Object>(mMap); 
  10.                 } 
  11.                 //賦值此時緩存集合給mapToWriteToDisk  
  12.                 mapToWriteToDisk = mMap; 
  13.                 ....... 
  14.                 synchronized (mLock) { 
  15.                     boolean changesMade = false
  16.                     //重點(diǎn):是否清空數(shù)據(jù) 
  17.                     if (mClear) { 
  18.                         if (!mMap.isEmpty()) { 
  19.                             changesMade = true
  20.                             //清空緩存中鍵值對信息 
  21.                             mMap.clear(); 
  22.                         } 
  23.                         mClear = false
  24.                     } 
  25.                     //循環(huán)mModified,將mModified中的數(shù)據(jù)更新到mMap中 
  26.                     for (Map.Entry<String, Object> e : mModified.entrySet()) { 
  27.                         String k = e.getKey(); 
  28.                         Object v = e.getValue(); 
  29.                         // "this" is the magic value for a removal mutation. In addition, 
  30.                         // setting a value to "null" for a given key is specified to be 
  31.                         // equivalent to calling remove on that key
  32.                         if (v == this || v == null) { 
  33.                             if (!mMap.containsKey(k)) { 
  34.                                 continue
  35.                             } 
  36.                             mMap.remove(k); 
  37.                         } else { 
  38.                             if (mMap.containsKey(k)) { 
  39.                                 Object existingValue = mMap.get(k); 
  40.                                 if (existingValue != null && existingValue.equals(v)) { 
  41.                                     continue
  42.                                 } 
  43.                             } 
  44.                             //注意:此時把鍵值對信息寫入到了緩存集合中 
  45.                             mMap.put(k, v); 
  46.                         } 
  47. ......... 
  48.                     } 
  49.                     //清空臨時集合 
  50.                     mModified.clear(); 
  51.                    ...... 
  52.                 } 
  53.             } 
  54.             return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners, 
  55.                     mapToWriteToDisk); 
  56.         } 
  • mModified就是我們本次要更新添加的鍵值對集合;
  • mClear是我們調(diào)用clear()方法的時候賦值的;
  • 大致流程就是:首先判斷是否需要清空內(nèi)存數(shù)據(jù),然后循環(huán)mModified集合,添加更新數(shù)據(jù)到內(nèi)存的鍵值對集合中;

6、commit方法

  1. public boolean commit() { 
  2.            ....... 
  3.            //更新數(shù)據(jù)到內(nèi)存 
  4.            MemoryCommitResult mcr = commitToMemory(); 
  5.            //更新數(shù)據(jù)到磁盤 
  6.            SharedPreferencesImpl.this.enqueueDiskWrite( 
  7.                mcr, null /* sync write on this thread okay */); 
  8.            try { 
  9.            //等待:等待磁盤更新數(shù)據(jù)完成 
  10.                mcr.writtenToDiskLatch.await(); 
  11.            } catch (InterruptedException e) { 
  12.                return false
  13.            } finally { 
  14.                if (DEBUG) { 
  15.                    Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration 
  16.                            + " committed after " + (System.currentTimeMillis() - startTime) 
  17.                            + " ms"); 
  18.                } 
  19.            } 
  20.            //執(zhí)行l(wèi)istener回調(diào) 
  21.            notifyListeners(mcr); 
  22.            return mcr.writeToDiskResult; 
  23.        } 
  • 首先apply沒有返回值,commit有返回值;
  • 其實(shí)apply執(zhí)行回調(diào)是和數(shù)據(jù)寫入磁盤并行執(zhí)行的,而commit方法執(zhí)行回調(diào)是等待磁盤寫入數(shù)據(jù)完成之后;

三、QueuedWork詳解

1、QueuedWork

QueuedWork這個類,因?yàn)閟p的初始化之后就是使用,前面看到,無論是apply還是commit方法都是通過QueuedWork來實(shí)現(xiàn)的;

QueuedWork是一個管理類,顧名思義,其中有一個隊(duì)列,對所有入隊(duì)的work進(jìn)行管理調(diào)度;

其中最重要的就是有一個HandlerThread

  1. private static Handler getHandler() { 
  2.        synchronized (sLock) { 
  3.            if (sHandler == null) { 
  4.                HandlerThread handlerThread = new HandlerThread("queued-work-looper"
  5.                        Process.THREAD_PRIORITY_FOREGROUND); 
  6.                handlerThread.start(); 
  7.                sHandler = new QueuedWorkHandler(handlerThread.getLooper()); 
  8.            } 
  9.            return sHandler; 
  10.        } 
  11.    } 

2、入隊(duì)queue

  1. // 如果是commit,則不能delay,如果是apply,則可以delay 
  2.    public static void queue(Runnable work, boolean shouldDelay) { 
  3.        Handler handler = getHandler(); 
  4.        synchronized (sLock) { 
  5.            sWork.add(work); 
  6.            if (shouldDelay && sCanDelay) { 
  7.                // 默認(rèn)delay的時間是100ms 
  8.                handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY); 
  9.            } else { 
  10.                handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN); 
  11.            } 
  12.        } 
  13.    } 

3、消息的處理

  1. private static class QueuedWorkHandler extends Handler { 
  2.        static final int MSG_RUN = 1; 
  3.        QueuedWorkHandler(Looper looper) { 
  4.            super(looper); 
  5.        } 
  6.        public void handleMessage(Message msg) { 
  7.            if (msg.what == MSG_RUN) { 
  8.                processPendingWork(); 
  9.            } 
  10.        } 
  11.    } 
  12.    private static void processPendingWork() { 
  13.        synchronized (sProcessingWork) { 
  14.            LinkedList<Runnable> work
  15.            synchronized (sLock) { 
  16.                work = (LinkedList<Runnable>) sWork.clone(); 
  17.                sWork.clear(); 
  18.                getHandler().removeMessages(QueuedWorkHandler.MSG_RUN); 
  19.            } 
  20.            if (work.size() > 0) { 
  21.                for (Runnable w : work) { 
  22.                    w.run(); 
  23.                } 
  24.            } 
  25.        } 
  26.    } 
  • 可以看到,調(diào)度非常簡單,內(nèi)部有一個sWork,需要執(zhí)行的時候遍歷所有的runnable執(zhí)行;
  • 對于apply操作,會有一定的延遲再去執(zhí)行work,但是對于commit操作,則會馬上觸發(fā)調(diào)度,而且并不僅僅是調(diào)度commit傳過來的那個任務(wù),而是馬上就調(diào)度隊(duì)列中所有的work;

4、waitToFinish

系統(tǒng)中很多地方會等待sp的寫入文件完成,等待方式是通過調(diào)用QueuedWork.waitToFinish();

  1. public static void waitToFinish() { 
  2.       Handler handler = getHandler(); 
  3.       synchronized (sLock) { 
  4.           // 移除所有消息,直接開始調(diào)度所有work 
  5.           if (handler.hasMessages(QueuedWorkHandler.MSG_RUN)) { 
  6.               handler.removeMessages(QueuedWorkHandler.MSG_RUN); 
  7.           } 
  8.           sCanDelay = false
  9.       } 
  10.       StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); 
  11.       try { 
  12.           // 如果是waitToFinish調(diào)用過來,則馬上執(zhí)行所有的work 
  13.           processPendingWork(); 
  14.       } finally { 
  15.           StrictMode.setThreadPolicy(oldPolicy); 
  16.       } 
  17.       try { 
  18.           // 在所有的work執(zhí)行完畢之后,還需要執(zhí)行Finisher 
  19.           // 前面在apply的時候有一步是QueuedWork.addFinisher(awaitCommit); 
  20.           // 其中的實(shí)現(xiàn)是等待sp文件的寫入完成 
  21.           // 如果沒有通過msg去調(diào)度而是通過waitToFinish,則那個runnable就會在這里被執(zhí)行 
  22.           while (true) { 
  23.               Runnable finisher; 
  24.               synchronized (sLock) { 
  25.                   finisher = sFinishers.poll(); 
  26.               } 
  27.               if (finisher == null) { 
  28.                   break; 
  29.               } 
  30.               finisher.run(); 
  31.           } 
  32.       } finally { 
  33.           sCanDelay = true
  34.       } 
  35.       ... 
  36.   } 

系統(tǒng)中對于四大組件的處理邏輯都在ActivityThread中實(shí)現(xiàn),在service/activity的生命周期的執(zhí)行中都會等待sp的寫入完成,正是通過調(diào)用QueuedWork.waitToFinish(),確保app的數(shù)據(jù)正確的寫入到disk;

5、sp使用的建議

  • 對數(shù)據(jù)實(shí)時性要求不高,盡量使用apply
  • 如果業(yè)務(wù)要求必須數(shù)據(jù)成功寫入,使用commit
  • 減少sp操作頻次,盡量一次commit把所有的數(shù)據(jù)都寫入完畢
  • 可以適當(dāng)考慮不要在主線程訪問sp
  • 寫入sp的數(shù)據(jù)盡量輕量級

總結(jié):

SharedPreferences的本身實(shí)現(xiàn)就是分為兩步,一步是內(nèi)存,一部是磁盤,而主線程又依賴SharedPreferences的寫入,所以可能當(dāng)io成為瓶頸的時候,App會因?yàn)镾haredPreferences變的卡頓,嚴(yán)重情況下會ANR,總結(jié)下來有以下幾點(diǎn):

  • 存放在xml文件中的數(shù)據(jù)會被裝在到內(nèi)存中,所以獲取數(shù)據(jù)很快
  • apply是異步操作,提交數(shù)據(jù)到內(nèi)存,并不會馬上提交到磁盤
  • commit是同步操作,會等待數(shù)據(jù)寫入到磁盤,并返回結(jié)果
  • 如果有同一個線程多次commit,則后面的要等待前面執(zhí)行結(jié)束
  • 如果多個線程對同一個sp并發(fā)commit,后面的所有任務(wù)會進(jìn)入到QueuedWork中排隊(duì)執(zhí)行,且都要等第一個執(zhí)行完畢

本文轉(zhuǎn)載自微信公眾號「Android開發(fā)編程」

【編輯推薦】

 

責(zé)任編輯:姜華 來源: Android開發(fā)編程
相關(guān)推薦

2021-09-08 06:51:52

AndroidRetrofit原理

2021-09-10 07:31:54

AndroidAppStartup原理

2021-09-30 07:36:51

AndroidViewDraw

2021-08-24 07:53:28

AndroidActivity生命周期

2021-09-15 07:31:33

Android窗口管理

2021-09-16 06:44:04

Android進(jìn)階流程

2021-09-24 08:10:40

Java 語言 Java 基礎(chǔ)

2021-09-17 06:55:50

AndroidLayoutView

2023-10-13 13:30:00

MySQL鎖機(jī)制

2021-09-18 06:56:01

JavaCAS機(jī)制

2022-09-05 22:22:00

Stream操作對象

2017-05-03 17:00:16

Android渲染機(jī)制

2021-09-09 06:55:43

AndroidViewDragHel原理

2021-09-04 07:29:57

Android

2021-09-01 06:48:16

AndroidGlide緩存

2024-12-30 08:02:40

2017-01-13 22:42:15

iosswift

2014-07-15 17:17:31

AdapterAndroid

2017-08-08 09:15:41

前端JavaScript頁面渲染

2014-06-13 11:08:52

Redis主鍵失效
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

精品国产成人在线影院| 97久久超碰精品国产| 中文字幕在线日韩 | 午夜视频福利在线| 国产精品尤物| 中文字幕久久亚洲| 在线观看视频在线观看| 成年男女免费视频网站不卡| 99精品久久久久久| 国产精品福利网站| 日韩激情综合网| 国产在线播放精品| 欧美网站一区二区| 欧美日韩中文字幕在线播放| 欧洲av在线播放| 日韩国产高清在线| 欧美大片免费观看在线观看网站推荐 | 精品久久久久久久久久久久包黑料| 国产手机免费视频| 国产对白叫床清晰在线播放| 国产伦精品一区二区三区视频青涩 | 国产午夜精品美女视频明星a级| 亚洲xxx在线观看| free性欧美16hd| 日本一区二区三级电影在线观看| 91影院未满十八岁禁止入内| 欧美日韩综合一区二区三区| 久久久久美女| 亚洲男人的天堂在线播放| 天堂在线中文在线| 欧美成人h版| 亚洲精品国产一区二区精华液| 乱色588欧美| 99国产精品久久久久久久成人| 久久久久国产精品一区三寸| 欧美激情在线观看| sm捆绑调教视频| 婷婷综合电影| 精品久久久久香蕉网| 日韩中文字幕a| 亚洲欧美电影| 亚洲成人免费av| 视频一区二区视频| 电影在线一区| 99国产精品久久久久久久久久 | 狠狠网亚洲精品| 欧美一级在线播放| 久久久久久天堂| 久久伦理在线| 一区二区亚洲欧洲国产日韩| 真人bbbbbbbbb毛片| 99热这里只有精品首页| 欧美一区二区三区视频在线观看| 欧美伦理视频在线观看| 筱崎爱全乳无删减在线观看 | 水蜜桃亚洲一二三四在线| 熟妇人妻中文av无码| 国产成人自拍网| 国产欧美精品一区二区三区介绍| 精品国产xxx| 亚洲一区二区三区四区五区午夜| 欧美精品电影免费在线观看| 青青草成人免费| 亚洲破处大片| 久久精品人人做人人爽| 久久精品日韩无码| 91综合在线| 精品国内亚洲在观看18黄| 国产视频123区| 欧美色婷婷久久99精品红桃| 一区二区三区四区视频| 性の欲びの女javhd| 国产一区二区三区天码| 亚洲新中文字幕| 中文字幕国产专区| 欧美视频免费| 色老头一区二区三区在线观看| 亚洲精品天堂网| 久久在线免费| 两个人的视频www国产精品| 午夜激情福利网| 欧美日韩在线大尺度| 久久久久国产精品免费| 国产成人在线播放视频| 久久久久免费| 国产欧美精品日韩精品| www.爱爱.com| 本田岬高潮一区二区三区| 国产精品日韩欧美一区二区| 五月天婷婷视频| 国产调教视频一区| 欧美一级免费在线观看| 久草成色在线| 欧美丝袜第一区| 91极品视频在线观看| 9999精品视频| 亚洲黄色有码视频| 九九九视频在线观看| 久久精品亚洲人成影院 | 国产亚洲依依| 国产精品久久久久久一区二区三区 | 欧美精品精品一区| 91成人在线观看喷潮蘑菇| 理论片一区二区在线| 国产一区二区三区精品久久久| 登山的目的在线| aa亚洲婷婷| 国产精品专区第二| 可以免费看毛片的网站| 国产日韩av一区| 996这里只有精品| 中文字幕乱码在线播放| 欧美男同性恋视频网站| 日韩av无码一区二区三区不卡| 欧美精品一区二区久久| 久久久女人电视剧免费播放下载 | 午夜久久99| 国产91亚洲精品| 国产黄色大片网站| 国产日韩欧美综合一区| 国产精品久久国产| 成人免费av电影| 亚洲国产成人久久综合| 肉色超薄丝袜脚交69xx图片| 国产精品试看| 99国产在线视频| 成a人片在线观看www视频| 亚洲成人精品一区二区| 日本精品一区在线| 欧美亚洲国产精品久久| 91高清视频免费| 亚洲第一页视频| 国产精品久久久久桃色tv| av网址在线观看免费| 成人台湾亚洲精品一区二区| 精品国产一区二区三区四区在线观看 | 国产尤物在线播放| 久久亚洲欧洲| 精品国产乱码久久久久久郑州公司| 秋霞午夜在线观看| 欧日韩精品视频| 特级西西人体wwwww| 欧美黄色一区二区| 91久久久在线| 97电影在线| 色婷婷综合视频在线观看| 精品视频站长推荐| 亚洲午夜精品久久久久久app| 成人国产精品久久久久久亚洲| 九九九伊在人线综合| 黄色成人av在线| 精品久久久久久无码人妻| 91精品国产成人观看| 国产一区视频在线| 成年人在线观看网站| 在线亚洲高清视频| 国产一级久久久久毛片精品| 可以看av的网站久久看| 欧美亚洲精品日韩| 欧美日韩视频网站| 亚洲天堂免费观看| 五月婷婷激情五月| 欧美激情一区不卡| 国产又黄又猛又粗又爽的视频| 国内精品久久久久久久久电影网 | 日韩和的一区二在线| 亚洲美女av在线| 中文字幕一区二区三区四区欧美| 久久亚洲精品国产精品紫薇 | 日韩在线观看一区二区| 欧美日韩一区二区三区在线视频| 欧美激情喷水| 国产亚洲精品一区二区| 中文字幕福利视频| 亚洲欧洲日韩综合一区二区| 涩多多在线观看| 欧美69视频| 成人免费在线看片| 欧美办公室脚交xxxx| 日韩精品在线免费观看视频| 亚洲va在线观看| 国产亚洲综合色| 亚洲欧美日韩综合网| 欧美 日韩 国产 一区| 国产91色在线|亚洲| 性欧美18~19sex高清播放| 亚洲日本aⅴ片在线观看香蕉| 在线观看国产精品入口男同| 亚洲色图制服诱惑| 中文字幕在线永久| 日韩国产精品久久| 日韩精品手机在线观看| 日韩超碰人人爽人人做人人添| 日本最新高清不卡中文字幕| 麻豆av在线免费看| 亚洲成年人在线播放| 在线观看日本网站| 亚洲三级小视频| 中文在线观看免费视频| 久久精品国语| 亚洲成年人专区| 欧美a一欧美| 国产精品三级久久久久久电影| 超碰caoporn久久| 亚洲精品久久7777777| 亚洲精品国产精品国自产网站按摩| 中文字幕字幕中文在线中不卡视频| av漫画在线观看| 视频一区欧美精品| 丁香婷婷综合激情| 中文精品一区二区| 91久久精品一区二区别| 性欧美18一19sex性欧美| 欧美老肥婆性猛交视频| 黄色免费在线播放| 日韩你懂的电影在线观看| 久久国产视频精品| 樱花草国产18久久久久| 中文字幕在线观看免费高清| fc2成人免费人成在线观看播放| 国产免费999| 亚洲精品乱码久久久久久蜜桃麻豆| 美女mm1313爽爽久久久蜜臀| 亚洲欧美日韩网| 国产一区二区三区在线观看| 性做久久久久久久久| 欧美大喷水吹潮合集在线观看| 欧美日韩导航| 亚洲www视频| 日韩中文视频| 亚洲91精品在线观看| 手机在线看福利| 伊人久久亚洲美女图片| 一本久久a久久精品vr综合| 日韩美女毛片| 97视频热人人精品| 欧美美女被草| 日本乱人伦a精品| xxx性欧美| 欧美精品一区在线播放| av资源网站在线观看| 亚洲毛片在线观看.| 人妻一区二区三区免费| 日韩一级二级三级| 91影院在线播放| 在线观看国产91| 亚洲天堂视频网站| 一区二区三区国产精品| 天天综合天天做| 中文字幕亚洲在| 免费一级特黄3大片视频| 国产日韩欧美高清| 在线不卡av电影| 久久综合久久综合九色| 亚洲精品女人久久久| 粉嫩蜜臀av国产精品网站| 国产999免费视频| 激情综合色播五月| 在线免费看污网站| 国内精品久久久久影院薰衣草 | 国产精品果冻传媒| 国产福利91精品一区| 午夜激情视频网| 激情图区综合网| 亚洲国产午夜精品| 国产一区二区在线观看视频| 五月天婷婷亚洲| 久久99这里只有精品| 色天使在线观看| 麻豆精品精品国产自在97香蕉| 中文字幕 91| 麻豆成人综合网| 天美一区二区三区| 国产久卡久卡久卡久卡视频精品| 日本中文字幕在线不卡| 国产成人高清在线| 国产精品扒开腿做爽爽爽a片唱戏| av在线播放成人| 人妻丰满熟妇av无码久久洗澡| 久久综合久久99| 日韩av片在线免费观看| 中文字幕一区二区三区av| 久久这里只有精品免费| 欧美日韩国产麻豆| 草莓视频18免费观看| 欧美日韩黄色影视| www.精品久久| 日韩av综合网站| 手机av在线免费观看| 亚洲人成电影在线观看天堂色| 福利小视频在线观看| 日韩在线观看成人| 国产偷倩在线播放| 久久久久久12| 国产精品粉嫩| 91综合免费在线| 日韩大胆成人| 香蕉久久免费影视| 国产一区美女| av在线无限看| 国产 日韩 欧美大片| 日本一级免费视频| 亚洲精品国产视频| 亚洲伊人成人网| 欧美精品 国产精品| 天天干天天插天天操| 色妞在线综合亚洲欧美| 爱看av在线入口| 91精品久久久久久久久| 女人抽搐喷水高潮国产精品| 一区一区视频| 亚洲经典自拍| 激情黄色小视频| 91麻豆文化传媒在线观看| 婷婷伊人五月天| 91黄色小视频| 欧美一区二区三区黄片 | 韩国av网站在线| 91chinesevideo永久地址| 国产一区高清| 久久久久久国产精品一区 | 亚洲欧美日韩爽爽影院| 伊人影院在线视频| 国产精品视频久久久久| 天堂成人娱乐在线视频免费播放网站 | 国产午夜视频在线播放| 欧美日韩一级片在线观看| 四虎精品在线| 欧美激情综合亚洲一二区| 久久女人天堂| 欧美日韩精品久久| 亚洲黄色在线| 波多野结衣电影免费观看| 国产人妖乱国产精品人妖| 亚洲国产精品午夜在线观看| 6080yy午夜一二三区久久| 免费人成在线观看网站| 亚洲18私人小影院| av不卡一区| 天天做天天爱天天高潮| 美女久久久精品| 蜜桃无码一区二区三区| 精品久久久久久久久国产字幕| 国产黄色小视频在线观看| 精品国产一区二区三区久久久狼| 精品免费av在线| 久久久久久国产精品mv| 性娇小13――14欧美| 星空大象在线观看免费播放| 亚洲一级二级在线| 精品国产无码一区二区| 久久综合免费视频影院| 高清在线一区二区| 中文字幕色一区二区| 久久99精品国产麻豆婷婷| 日本视频在线免费| 欧美精品v国产精品v日韩精品 | 亚洲成年网站在线观看| 女人天堂av在线播放| 粉嫩av免费一区二区三区| 亚洲男女av一区二区| 女人扒开腿免费视频app| 樱花影视一区二区| 亚洲精品国偷拍自产在线观看蜜桃| 久久大大胆人体| 精品一区二区三区在线观看视频| 成人午夜免费剧场| 国产乱码精品一区二区三区忘忧草| 欧美三级黄色大片| 日韩三级在线观看| 日本电影在线观看| 国产精品初高中精品久久| 亚洲国产欧美国产综合一区| 国产精品第七页| 色综合色综合色综合色综合色综合| 蜜桃免费在线| 国产精品丝袜一区二区三区| 亚洲国产不卡| 佐佐木明希电影| 精品久久久久久久久久久| 黄上黄在线观看| 国产精品免费在线免费| 欧美国产激情| 亚洲啪av永久无码精品放毛片 | 亚洲一区二区日韩| 台湾佬美性中文| 精品国产乱码久久久久酒店| 美女毛片在线看| 91精品久久久久久久久中文字幕| 综合久久精品| 国产国语老龄妇女a片| 日韩欧美视频一区二区三区| 日韩成人影视| av资源一区二区| 久久福利一区| 日韩一区二区不卡视频| 亚洲精品国产电影| 日本成人在线网站| www.av片| 国产精品乱人伦中文|