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 it
24                 return sp;
25             }
26             File backup = makeBackupFile(prefsFile);
27             if (backup.exists()) {
28                 prefsFile.delete();
29                 backup.renameTo(prefsFile);
30             }
31
32             // Debugging
33             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.java

52 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.this
58     //  - acquire mWritingToDiskLock before EditorImpl.this
59
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.java

273    public Editor edit() {
274        // TODO: remove the need to call awaitLoadedLocked() when
275        // requesting an editor.  will require some work on the
276        // 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        }
284
285        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;
306
307        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 made
389        private MemoryCommitResult commitToMemory() {
390            MemoryCommitResult mcr = new MemoryCommitResult();
391            synchronized (SharedPreferencesImpl.this) {
392                // We optimistically don‘t make a deep copy until
393                // a memory commit comes in when we‘re already
394                // writing to disk.
395                if (mDiskWritesInFlight > 0) {
396                    // We can‘t modify our mMap as a currently
397                    // in-flight write owns it.  Clone it before
398                    // modifying it.
399                    // noinspection unchecked
400                    mMap = new HashMap<String, Object>(mMap);
401                }
402                mcr.mapToWriteToDisk = mMap;
403                mDiskWritesInFlight++;
404
405                boolean hasListeners = mListeners.size() > 0;
406                if (hasListeners) {
407                    mcr.keysModified = new ArrayList<String>();
408                    mcr.listeners =
409                            new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
410                }
411
412                synchronized (this) {
413                    if (mClear) {
414                        if (!mMap.isEmpty()) {
415                            mcr.changesMade = true;
416                            mMap.clear();
417                        }
418                        mClear = false;
419                    }
420
421                    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 mutation
425                            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 = mMap.get(k);
433                                if (existingValue != null && existingValue.equals(v)) {
434                                    continue;
435                                }
436                            }
437                            mMap.put(k, v);
438                        }
439
440                        mcr.changesMade = true;
441                        if (hasListeners) {
442                            mcr.keysModified.add(k);
443                        }
444                    }
445
446                    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 read
566        if (mFile.exists()) {
567            if (!mcr.changesMade) {
568                // If the file already exists, but no changes were
569                // made to the underlying map, it‘s wasteful to
570                // re-write the file.  Return as if we wrote it
571                // 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 " + mFile
578                          + " to backup file " + mBackupFile);
579                    mcr.setDiskWriteResult(false);
580                    return;
581                }
582            } else {
583                mFile.delete();
584            }
585        }
586
587        // Attempt to write the file, delete the backup and return true as atomically as
588        // possible.  If any exception occurs, delete the new file; next time we will restore
589        // 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 nothing
608            }
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 file
619        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使用时,单例,线程安全,存在缓存(二次加载快速)。

时间: 2024-07-29 00:25:11

SharedPreferences小探的相关文章

[那些你所不知道的鬼畜写法]数据结构小探

我有种要出系列的节奏? 这次记一下我所遇到过的数据结构一些神奇的维护方法:  (只是记一下而已,也许会仔细讲讲) 1.差分大法 出自 zkw 大神 <统计的力量——线段树全接触>,运用面虽然不广,但每次用出来威力都是相当大 机智的减少你的代码量 2.保存操作而不是值 出自 clj 大神 在 WC 上对可追溯数据结构的论文(跪跪跪跪),虽然 OI 中应该(?)不会用到吗,但是思路还是很好的 只保存操作在很多时候效果非凡,以时间为权值维护一棵 splay 或是别的支持区间操作的平衡树,然后不但支持

数独解法小探

数独的游戏要求在一个9X9的格子内填入1~9的数字,使得每一行,每一列,以及九个3X3的子区域内都没有重复的数字. 稍作思索,我写出了第一种解法.从事后查询维基百科1来看,这种方法可以称之为回溯法.思路很简单,依次扫描每一个待填数字的空格: 1. 在第一个空格里面填上“1”,检查这个数字是否合法(其所在的行.列,以及3X3的子区域里不存在重复的数字).如果合法,则前进到第二个格子.否则,在这个格子里继续试2,3,… ,直到合法为止. 2. 在第二个格子里面继续填数字,从“1”开始试起,直到找到一

Linux——Virtualenv和pip小探

转载自:http://mengzhuo.org/blog/virtualenv%E5%92%8Cpip%E5%B0%8F%E6%8E%A2.html 本文献给那些看着参差不齐的中文文档/教程,但还在坚持折腾以上两个工具的童鞋. 声明:本人也是菜鸟,真正的有用的概念解释,请参看官方文档,以下都是我的个人理解. virtualenv 这里是导言吗? 用过Python的同学,肯定会对Python及程序的版本之间经常更换的api感到痛苦不以.就拿我折腾的Django来说吧,公司服务器上跑的是Django

爬虫小探-Python3 urllib.request获取页面数据

使用Python3 urllib.request中的Requests()和urlopen()方法获取页面源码,并用re正则进行正则匹配查找需要的数据. #forex.py#coding:utf-8 ''' urllib.request.urlopen() function in Python 3 is equivalent to urllib2.urlopen() in Python2 urllib.request.Request() function in Python 3 is equiva

KVO 小探

#import "ViewController.h" @interface ViewController () @property (copy, nonatomic)  NSString *name; @property (assign, nonatomic) NSInteger age;  或者你这样写 { NSString *_name; NSInteger _age; } @end @implementation ViewController - (void)viewDidLoa

REG小探

根键名称缩写对照表   常用数据类型 原文地址:https://www.cnblogs.com/feiyucha/p/10366927.html

设计模式——结构型模式

设计模式的另一大类型为结构型.共收录了7个模式,分别为适配器模式.桥接模式.组合模式.装饰模式.外观模式.享元模式.代理模式.下面从特点和使用两方面小探,欢迎交流!      适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.[大话设计模式]        特点:需要交流的两方,在数据和行为都匹配,但接口不符时,我们应该考虑用适配器,目的是促使交流和复用.可以理解为入乡随俗,相同的意思,不同的表达

QtQuick 技巧

QtQuick 技巧 dpi property real dpi: Screen.pixelDensity.toFixed(2) 常用的 qmlproject /* File generated by Qt Creator */ import QmlProject 1.1 Project { mainFile: "main.qml" /* Include .qml, .js, and image files from current directory and subdirectori

初探Java8lambda表达式

要说现在什么火, 估计函数式编程算一个吧,看看人家javascript写起来多爽,java呢?一切皆对象.好像离着函数式编程挺远的,不过在java8中我们终于迎来了类似函数式编程-Java风格的lambda表达式,在用lambda重构你的代码后,啥感觉? 倍爽! 这篇博客我们就来小探一下java8的lambda表达式,领略一下lambda表达式的风骚! lambda表达式语法 lambda的语句可以用以下伪代码表示,很简单,不过还有很多更简单的表达方式, (Type ...argument) -