自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android源碼進階之深入理解SharedPreference原理機制

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

[[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來對儲存的文件進行操作 
  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ù)是鍵名,第二個是默認值 
  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ù)相同;
  • 刪除掉文件后,程序在沒有完全退出停止運行的情況下,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); 

實際上context的真正實現(xiàn)類是ContextImp,所以進入到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.   //重點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.           //重點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實現(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.            //重點:喚醒所有以mLock鎖的等待線程 
  61.            mLock.notifyAll(); 
  62.        } 
  63.    } 
  • 首先判斷備份文件是否存在,如果存在,就更該備份文件的后綴名;接著就開始讀取數(shù)據(jù),然后將讀取的數(shù)據(jù)賦值給全局變量存儲文件鍵值對的mMap對象,并且更新修改時間以及文件大小變量;
  • 喚醒所有以mLock為鎖的等待線程;
  • 到此為止,初始化SP對象就算完成了,其實可以看出來就是一個二級緩存流程:磁盤到內(nèi)存;

3、get獲取SP中的鍵值對

  1. @Nullable 
  2.    public String getString(String key, @Nullable String defValue) { 
  3.        synchronized (mLock) { 鎖判斷 
  4.            awaitLoadedLocked(); //等待機制 
  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à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.                     //重點:是否清空數(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有返回值;
  • 其實apply執(zhí)行回調(diào)是和數(shù)據(jù)寫入磁盤并行執(zhí)行的,而commit方法執(zhí)行回調(diào)是等待磁盤寫入數(shù)據(jù)完成之后;

三、QueuedWork詳解

1、QueuedWork

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

QueuedWork是一個管理類,顧名思義,其中有一個隊列,對所有入隊的work進行管理調(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、入隊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.                // 默認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)度隊列中所有的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.           // 其中的實現(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中實現(xiàn),在service/activity的生命周期的執(zhí)行中都會等待sp的寫入完成,正是通過調(diào)用QueuedWork.waitToFinish(),確保app的數(shù)據(jù)正確的寫入到disk;

5、sp使用的建議

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

總結(jié):

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

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

Java 語言 Java 基礎(chǔ)

2021-09-15 07:31:33

Android窗口管理

2021-09-16 06:44:04

Android進階流程

2021-09-17 06:55:50

AndroidLayoutView

2021-09-18 06:56:01

JavaCAS機制

2023-10-13 13:30:00

MySQL鎖機制

2022-09-05 22:22:00

Stream操作對象

2021-09-09 06:55:43

AndroidViewDragHel原理

2017-05-03 17:00:16

Android渲染機制

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-08-08 09:15:41

前端JavaScript頁面渲染

2021-10-26 17:52:52

Android插件化技術(shù)

2014-06-13 11:08:52

Redis主鍵失效
點贊
收藏

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