首页 > 代码库 > SharedPreferences小探

SharedPreferences小探

突然想到个问题,SharedPreferences线程安全么?有没有使用缓存相关的技术?

 

首先想到的是Activity里面的:

public abstract SharedPreferences getSharedPreferences(String name, int mode);

  

android.content.Context中,我们首先找到简单的解释:

The single SharedPreferences instance that can be used to retrieve and modify the preference values.

关键字:单例,继续

 1    // android.app.ContextImpl.java 2    private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs = new HashMap<String, SharedPreferencesImpl>();
3 @Override 4 public SharedPreferences getSharedPreferences(String name, int mode) { 5 SharedPreferencesImpl sp; 6 File prefsFile; 7 boolean needInitialLoad = false; 8 synchronized (sSharedPrefs) { 9 sp = sSharedPrefs.get(name);10 if (sp != null && !sp.hasFileChangedUnexpectedly()) {11 return sp;12 }13 prefsFile = getSharedPrefsFile(name);14 if (sp == null) {15 sp = new SharedPreferencesImpl(prefsFile, mode, null);16 sSharedPrefs.put(name, sp);17 needInitialLoad = true;18 }19 }20 21 synchronized (sp) {22 if (needInitialLoad && sp.isLoaded()) {23 // lost the race to load; another thread handled it24 return sp;25 }26 File backup = makeBackupFile(prefsFile);27 if (backup.exists()) {28 prefsFile.delete();29 backup.renameTo(prefsFile);30 }31 32 // Debugging33 if (prefsFile.exists() && !prefsFile.canRead()) {34 Log.w(TAG, "Attempt to read preferences file " + prefsFile + " without permission");35 }36 37 Map map = null;38 FileStatus stat = new FileStatus();39 if (FileUtils.getFileStatus(prefsFile.getPath(), stat) && prefsFile.canRead()) {40 try {41 FileInputStream str = new FileInputStream(prefsFile);42 map = XmlUtils.readMapXml(str);43 str.close();44 } catch (org.xmlpull.v1.XmlPullParserException e) {45 Log.w(TAG, "getSharedPreferences", e);46 } catch (FileNotFoundException e) {47 Log.w(TAG, "getSharedPreferences", e);48 } catch (IOException e) {49 Log.w(TAG, "getSharedPreferences", e);50 }51 }52 sp.replace(map, stat);53 }54 return sp;55 }

认识1:getSharedPreferences通过Map保证SharedPreferences单例。

继续

//SharedPreferencesImpl.java52 final class More ...SharedPreferencesImpl implements SharedPreferences {53     private static final String TAG = "SharedPreferencesImpl";54     private static final boolean DEBUG = false;55 56     // Lock ordering rules:57     //  - acquire SharedPreferencesImpl.this before EditorImpl.this58     //  - acquire mWritingToDiskLock before EditorImpl.this59 60     private final File mFile;61     private final File mBackupFile;62     private final int mMode;63 64     private Map<String, Object> mMap;     // guarded by ‘this‘

mMap是不是editor的缓存?

//SharedPreferencesImpl.java273    public Editor edit() {274        // TODO: remove the need to call awaitLoadedLocked() when275        // requesting an editor.  will require some work on the276        // Editor, but then we should be able to do:277        //278        //      context.getSharedPreferences(..).edit().putString(..).apply()279        //280        // ... all without blocking.281        synchronized (this) {282            awaitLoadedLocked();283        }284285        return new EditorImpl();286    }303    public final class EditorImpl implements Editor {304        private final Map<String, Object> mModified = Maps.newHashMap();305        private boolean mClear = false;306307        public Editor putString(String key, String value) {308            synchronized (this) {309                mModified.put(key, value);310                return this;311            }312        }

认识2:每次edit会new EditorImpl ,并且EditorImpl 自带mModified缓存。

388        // Returns true if any changes were made389        private MemoryCommitResult commitToMemory() {390            MemoryCommitResult mcr = new MemoryCommitResult();391            synchronized (SharedPreferencesImpl.this) {392                // We optimistically don‘t make a deep copy until393                // a memory commit comes in when we‘re already394                // writing to disk.395                if (mDiskWritesInFlight > 0) {396                    // We can‘t modify our mMap as a currently397                    // in-flight write owns it.  Clone it before398                    // modifying it.399                    // noinspection unchecked400                    mMap = new HashMap<String, Object>(mMap);401                }402                mcr.mapToWriteToDisk = mMap;403                mDiskWritesInFlight++;404405                boolean hasListeners = mListeners.size() > 0;406                if (hasListeners) {407                    mcr.keysModified = new ArrayList<String>();408                    mcr.listeners =409                            new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());410                }411412                synchronized (this) {413                    if (mClear) {414                        if (!mMap.isEmpty()) {415                            mcr.changesMade = true;416                            mMap.clear();417                        }418                        mClear = false;419                    }420421                    for (Map.Entry<String, Object> e : mModified.entrySet()) {422                        String k = e.getKey();423                        Object v = e.getValue();424                        if (v == this) {  // magic value for a removal mutation425                            if (!mMap.containsKey(k)) {426                                continue;427                            }428                            mMap.remove(k);429                        } else {430                            boolean isSame = false;431                            if (mMap.containsKey(k)) {432                                Object existingValue =http://www.mamicode.com/ mMap.get(k);433                                if (existingValue != null && existingValue.equals(v)) {434                                    continue;435                                }436                            }437                            mMap.put(k, v);438                        }439440                        mcr.changesMade = true;441                        if (hasListeners) {442                            mcr.keysModified.add(k);443                        }444                    }445446                    mModified.clear();447                }448            }449            return mcr;450        }452        public boolean commit() {453            MemoryCommitResult mcr = commitToMemory();454            SharedPreferencesImpl.this.enqueueDiskWrite(455                mcr, null /* sync write on this thread okay */);456            try {457                mcr.writtenToDiskLatch.await();458            } catch (InterruptedException e) {459                return false;460            }461            notifyListeners(mcr);462            return mcr.writeToDiskResult;463        }
commit时,editor先把mModified同步到mMap,然后再写入File。
认识3:editor.commit是线程安全的。
564    private void writeToFile(MemoryCommitResult mcr) {565        // Rename the current file so it may be used as a backup during the next read566        if (mFile.exists()) {567            if (!mcr.changesMade) {568                // If the file already exists, but no changes were569                // made to the underlying map, it‘s wasteful to570                // re-write the file.  Return as if we wrote it571                // out.572                mcr.setDiskWriteResult(true);573                return;574            }575            if (!mBackupFile.exists()) {576                if (!mFile.renameTo(mBackupFile)) {577                    Log.e(TAG, "Couldn‘t rename file " + mFile578                          + " to backup file " + mBackupFile);579                    mcr.setDiskWriteResult(false);580                    return;581                }582            } else {583                mFile.delete();584            }585        }586587        // Attempt to write the file, delete the backup and return true as atomically as588        // possible.  If any exception occurs, delete the new file; next time we will restore589        // from the backup.590        try {591            FileOutputStream str = createFileOutputStream(mFile);592            if (str == null) {593                mcr.setDiskWriteResult(false);594                return;595            }596            XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);597            FileUtils.sync(str);598            str.close();599            ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);600            try {601                final StructStat stat = Libcore.os.stat(mFile.getPath());602                synchronized (this) {603                    mStatTimestamp = stat.st_mtime;604                    mStatSize = stat.st_size;605                }606            } catch (ErrnoException e) {607                // Do nothing608            }609            // Writing was successful, delete the backup file if there is one.610            mBackupFile.delete();611            mcr.setDiskWriteResult(true);612            return;613        } catch (XmlPullParserException e) {614            Log.w(TAG, "writeToFile: Got exception:", e);615        } catch (IOException e) {616            Log.w(TAG, "writeToFile: Got exception:", e);617        }618        // Clean up an unsuccessfully written file619        if (mFile.exists()) {620            if (!mFile.delete()) {621                Log.e(TAG, "Couldn‘t clean up partially-written file " + mFile);622            }623        }624        mcr.setDiskWriteResult(false);625    }

认识4:存在mBackupFile,直接操作mFile,如果出现问题,恢复备份。

 

总结:SharedPreferences使用时,单例,线程安全,存在缓存(二次加载快速)。

SharedPreferences小探