成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

[[429060]]

前言

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

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

一、SharedPreferences簡(jiǎn)單使用

1、創(chuàng)建

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

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

2、寫入

  1. //可以創(chuàng)建一個(gè)新的SharedPreference來對(duì)儲(chǔ)存的文件進(jìn)行操作 
  2. SharedPreferences sp=context.getSharedPreferences("名稱", Context.MODE_PRIVATE); 
  3. //像SharedPreference中寫入數(shù)據(jù)需要使用Editor 
  4. SharedPreference.Editor editor = sp.edit(); 
  5. //類似鍵值對(duì) 
  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也會(huì)被刪除;

3、讀取

  1. SharedPreference sp=context.getSharedPreferences("名稱", Context.MODE_PRIVATE); 
  2. //第一個(gè)參數(shù)是鍵名,第二個(gè)是默認(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ù)的時(shí)候一定要先clear()、再commit(),不能直接刪除xml文件;

  1. SharedPreference sp=getSharedPreferences("名稱", Context.MODE_PRIVATE); 
  2. SharedPrefence.Editor editor=sp.edit(); 
  3. editor.clear(); 
  4. editor.commit(); 
  • getSharedPreference() 不會(huì)生成文件,這個(gè)大家都知道;
  • 刪除掉文件后,再次執(zhí)行commit(),刪除的文件會(huì)重生,重生文件的數(shù)據(jù)和刪除之前的數(shù)據(jù)相同;
  • 刪除掉文件后,程序在沒有完全退出停止運(yùn)行的情況下,Preferences對(duì)象所存儲(chǔ)的內(nèi)容是不變的,雖然文件沒有了,但數(shù)據(jù)依然存在;程序完全退出停止之后,數(shù)據(jù)才會(huì)丟失;
  • 清除SharedPreferences數(shù)據(jù)一定要執(zhí)行editor.clear(),editor.commit(),不能只是簡(jiǎn)單的刪除文件,這也就是最后的結(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鍵值對(duì)存入集合中 
  16.                mSharedPrefsPaths.put(name, file); 
  17.            } 
  18.        } 
  19.        return getSharedPreferences(file, mode); 
  20.    } 

ArrayMap<String, File> mSharedPrefsPaths;對(duì)象是用來存儲(chǔ)SharedPreference文件名稱和對(duì)應(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)建對(duì)象

  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.       //獲取緩存對(duì)象(或者創(chuàng)建緩存對(duì)象) 
  9.           final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked(); 
  10.           //通過鍵file從緩存對(duì)象中獲取Sp對(duì)象 
  11.           sp = cache.get(file); 
  12.           //如果是null,就說明緩存中還沒后該文件的sp對(duì)象 
  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)中存儲(chǔ)的所有包名以及對(duì)應(yīng)的文件,這就是每個(gè)sp文件只有一個(gè)對(duì)應(yīng)的SharedPreferencesImpl實(shí)現(xiàn)對(duì)象原因

流程:

  • 獲取緩存區(qū),從緩存區(qū)中獲取數(shù)據(jù),看是否存在sp對(duì)象,如果存在就直接返回
  • 如果不存在,那么就從磁盤獲取數(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對(duì)象,如果之前沒有使用過,就需要?jiǎng)?chuàng)建一個(gè)對(duì)象了,通過方法checkMode(mode);
  • 先檢查mode是否是三種模式,然后通過sp = new SharedPreferencesImpl(file, mode);
  • 創(chuàng)建對(duì)象,并將創(chuàng)建的對(duì)象放到系統(tǒng)的packagePrefs中,方便以后直接獲取;
  1. SharedPreferencesImpl(File file, int mode) { 
  2.         mFile = file; //存儲(chǔ)文件 
  3.         //備份文件(災(zāi)備文件) 
  4.         mBackupFile = makeBackupFile(file); 
  5.         //模式 
  6.         mMode = mode; 
  7.         //是否加載過了 
  8.         mLoaded = false
  9.         // 存儲(chǔ)文件內(nèi)的鍵值對(duì)信息 
  10.         mMap = null
  11.         //從名字可以知道是:開始加載數(shù)據(jù)從磁盤 
  12.         startLoadFromDisk(); 
  13.     } 
  • 主要是設(shè)置了幾個(gè)參數(shù),mFile 是原始文件;mBackupFile 是后綴.bak的備份文件;
  • mLoaded標(biāo)識(shí)是否正在加載修改文件;
  • mMap用來存儲(chǔ)sp文件中的數(shù)據(jù),存儲(chǔ)時(shí)候也是鍵值對(duì)形式,獲取時(shí)候也是通過這個(gè)獲取,這就是表示每次使用sp的時(shí)候,都是將數(shù)據(jù)寫入內(nèi)存,也就是sp數(shù)據(jù)存儲(chǔ)數(shù)據(jù)快的原因,所以sp文件不能存儲(chǔ)大量數(shù)據(jù),否則執(zhí)行時(shí)候很容易會(huì)導(dǎo)致OOM;
  • mThrowable加載文件時(shí)候報(bào)的錯(cuò)誤;
  • 下面就是加載數(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賦值給全局的存儲(chǔ)文件鍵值對(duì)的mMap對(duì)象 
  53.                mMap = map; 
  54.                //更新內(nèi)存的修改時(shí)間以及文件大小 
  55.                mStatTimestamp = stat.st_mtime; 
  56.                mStatSize = stat.st_size; 
  57.            } else { 
  58.                mMap = new HashMap<>(); 
  59.            } 
  60.            //重點(diǎn):?jiǎn)拘阉幸詍Lock鎖的等待線程 
  61.            mLock.notifyAll(); 
  62.        } 
  63.    } 
  • 首先判斷備份文件是否存在,如果存在,就更該備份文件的后綴名;接著就開始讀取數(shù)據(jù),然后將讀取的數(shù)據(jù)賦值給全局變量存儲(chǔ)文件鍵值對(duì)的mMap對(duì)象,并且更新修改時(shí)間以及文件大小變量;
  • 喚醒所有以mLock為鎖的等待線程;
  • 到此為止,初始化SP對(duì)象就算完成了,其實(shí)可以看出來就是一個(gè)二級(jí)緩存流程:磁盤到內(nèi)存;

3、get獲取SP中的鍵值對(duì)

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

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

4、putXXX以及apply源碼

  1. public Editor edit() { 
  2.         //跟getXXX原理一樣 
  3.         synchronized (mLock) { 
  4.             awaitLoadedLocked(); 
  5.         } 
  6.         //返回EditorImp對(duì)象 
  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.                 //賦值此時(shí)緩存集合給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.                             //清空緩存中鍵值對(duì)信息 
  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.                             //注意:此時(shí)把鍵值對(duì)信息寫入到了緩存集合中 
  45.                             mMap.put(k, v); 
  46.                         } 
  47. ......... 
  48.                     } 
  49.                     //清空臨時(shí)集合 
  50.                     mModified.clear(); 
  51.                    ...... 
  52.                 } 
  53.             } 
  54.             return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners, 
  55.                     mapToWriteToDisk); 
  56.         } 
  • mModified就是我們本次要更新添加的鍵值對(duì)集合;
  • mClear是我們調(diào)用clear()方法的時(shí)候賦值的;
  • 大致流程就是:首先判斷是否需要清空內(nèi)存數(shù)據(jù),然后循環(huán)mModified集合,添加更新數(shù)據(jù)到內(nèi)存的鍵值對(duì)集合中;

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

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

其中最重要的就是有一個(gè)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的時(shí)間是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)度非常簡(jiǎn)單,內(nèi)部有一個(gè)sWork,需要執(zhí)行的時(shí)候遍歷所有的runnable執(zhí)行;
  • 對(duì)于apply操作,會(huì)有一定的延遲再去執(zhí)行work,但是對(duì)于commit操作,則會(huì)馬上觸發(fā)調(diào)度,而且并不僅僅是調(diào)度commit傳過來的那個(gè)任務(wù),而是馬上就調(diào)度隊(duì)列中所有的work;

4、waitToFinish

系統(tǒng)中很多地方會(huì)等待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的時(shí)候有一步是QueuedWork.addFinisher(awaitCommit); 
  20.           // 其中的實(shí)現(xiàn)是等待sp文件的寫入完成 
  21.           // 如果沒有通過msg去調(diào)度而是通過waitToFinish,則那個(gè)runnable就會(huì)在這里被執(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)中對(duì)于四大組件的處理邏輯都在ActivityThread中實(shí)現(xiàn),在service/activity的生命周期的執(zhí)行中都會(huì)等待sp的寫入完成,正是通過調(diào)用QueuedWork.waitToFinish(),確保app的數(shù)據(jù)正確的寫入到disk;

5、sp使用的建議

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

總結(jié):

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

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

本文轉(zhuǎn)載自微信公眾號(hào)「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-24 08:10:40

Java 語言 Java 基礎(chǔ)

2021-09-16 06:44:04

Android進(jìn)階流程

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操作對(duì)象

2021-09-09 06:55:43

AndroidViewDragHel原理

2017-05-03 17:00:16

Android渲染機(jī)制

2021-09-04 07:29:57

Android

2021-09-01 06:48:16

AndroidGlide緩存

2014-07-15 17:17:31

AdapterAndroid

2024-12-30 08:02:40

2017-01-13 22:42:15

iosswift

2017-08-08 09:15:41

前端JavaScript頁面渲染

2014-06-13 11:08:52

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

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

主站蜘蛛池模板: 免费观看色 | 亚洲精品国产成人 | 欧美亚洲一级 | 99小视频| 亚洲视频三区 | 欧洲精品码一区二区三区免费看 | 亚洲精品一区二区三区四区高清 | 成人精品高清 | 国产女人与拘做视频免费 | 午夜一级黄色片 | 精品成人在线 | 一区二区日韩 | 国产免费观看一级国产 | 欧美日韩在线一区二区 | 在线免费观看日本视频 | 精品久久久久久久久久久久久久久久久 | 男女爱爱福利视频 | 日本韩国欧美在线观看 | 黄网站在线播放 | 激情视频网站 | 日中文字幕在线 | 国产黄色网址在线观看 | 日韩欧美视频 | 欧美 日韩 中文 | 成年人在线视频 | 午夜爽爽爽男女免费观看影院 | 日韩欧美中文字幕在线观看 | 久久9热| 欧美日韩一二区 | 中文字幕在线一区二区三区 | 久草成人 | 成人精品鲁一区一区二区 | 欧美福利| 精品二区 | 伊人网国产 | 亚洲天天干 | 亚洲国产片| h片在线看| 日韩精品成人网 | 麻豆精品国产91久久久久久 | 日韩不卡三区 |