Activity之SharedPreferences探究
来源:互联网 发布:软件定义存储产品 编辑:程序博客网 时间:2024/05/02 04:57
先说说SharedPreferences的定义:
在frameworks\base\core\java\android\content\SharedPreferences.java 这个文件里定义了SharedPreferences.的Interface有其二段原文需要说说:
第一段:
Interface for accessing and modifying preference data returned by {@linkContext#getSharedPreferences}. 这说明Preferences其实是实现了对数据的访问和修改的。
第二段
For any particular set of preferences, there is a single instance of this class that all clients share. 这说明一个特定的preferences,其实是共多个客户共享的。
通过上面两段英文说明,大致就知道SharedPreferences是什么了、是干什么的了。下面我一步一步来解开SharedPreferences秘密。
刚才说了SharedPreferences.java 只是定义了SharedPreferences的一个接口,其实真正实现它的是在这里:SharedPreferencesImpl.java。我们来看他到底在做什么:
首先看他的构造函数:
SharedPreferencesImpl(File file, int mode) { mFile = file; mBackupFile = makeBackupFile(file); mMode = mode; mLoaded = false; mMap = null; startLoadFromDisk(); }这里我们关注这个函数的第一个参数:file. 传递了一个文件。这个file是如何创建的,我们先不说,后面再议。 我们现在只关注SharedPreferencesImpl.的实现。
再看一个函数:
public Editor edit() { // TODO: remove the need to call awaitLoadedLocked() when // requesting an editor. will require some work on the // Editor, but then we should be able to do: // // context.getSharedPreferences(..).edit().putString(..).apply() // // ... all without blocking. synchronized (this) { awaitLoadedLocked(); } return new EditorImpl(); }
当获取一个SharedPreferences实例时候,就需要调用这个函数才能获得这个SharedPreferences的操作。返回值是一个 Editor。那么对一个SharedPreferences的写数据、读数据等操作都是对这个SharedPreferences的Editor了,所以我们重点来看看Editor到底做了些什么。
先看看这个Editor的实现类EditorImpl其实是SharedPreferencesImpl的一个内部Final的类(下面给出一部分代码):
public final class EditorImpl implements Editor { private final Map<String, Object> mModified = Maps.newHashMap(); private boolean mClear = false; public Editor putString(String key, String value) { synchronized (this) { mModified.put(key, value); return this; } } public Editor putStringSet(String key, Set<String> values) { synchronized (this) { mModified.put(key, (values == null) ? null : new HashSet<String>(values)); return this; } } public Editor putInt(String key, int value) { synchronized (this) { mModified.put(key, value); return this; } }... ...这里我们关注他里面定义了一个HashMap的私有变量mModified。再看看putInt()z这些函数就知道,对SharedPreferences写入数据,其实就是对mModified这个HashMap加入数据。你会说,不会吧,数据写入HashMap能够永久保存吗?当然不会。 不慌,我们来看看这个内部类的一个函数:
public void apply() { final MemoryCommitResult mcr = commitToMemory(); final Runnable awaitCommit = new Runnable() { public void run() { try { mcr.writtenToDiskLatch.await(); } catch (InterruptedException ignored) { } } }; QueuedWork.add(awaitCommit); Runnable postWriteRunnable = new Runnable() { public void run() { awaitCommit.run(); QueuedWork.remove(awaitCommit); } }; SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable); // Okay to notify the listeners before it's hit disk // because the listeners should always get the same // SharedPreferences instance back, which has the // changes reflected in memory. notifyListeners(mcr); }重点看到里面有一行代码:SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);那这个函数做了些什么呢?
private void enqueueDiskWrite(final MemoryCommitResult mcr, final Runnable postWriteRunnable) { final Runnable writeToDiskRunnable = new Runnable() { public void run() { synchronized (mWritingToDiskLock) { writeToFile(mcr); } synchronized (SharedPreferencesImpl.this) { mDiskWritesInFlight--; } if (postWriteRunnable != null) { postWriteRunnable.run(); } } }; final boolean isFromSyncCommit = (postWriteRunnable == null); // Typical #commit() path with fewer allocations, doing a write on // the current thread. if (isFromSyncCommit) { boolean wasEmpty = false; synchronized (SharedPreferencesImpl.this) { wasEmpty = mDiskWritesInFlight == 1; } if (wasEmpty) { writeToDiskRunnable.run(); return; } } QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable); }首先,这个函数ANDROID给了一段说明我们来看看: Enqueue an already-committed-to-memory result to be written to disk. 好像是说把数据写道DISK里面????
我们注意这个函数定义的一个Runnable的变量:writeToDiskRunnable它里面执行的重要代码是writeToFile(mcr);估计你看到这里就觉得有明白,他要干什么呢!的确就如你想的一样,其实是把刚刚存储在一个HashMap的数据写道一个文件里。 不信的话,我们继续看看writeToFile(mcr);这个函数干了些什么。
writeToFile(mcr);这个函数里面有这样一段重要的代码,你看了就彻底明白了,
FileOutputStream str = createFileOutputStream(mFile); if (str == null) { mcr.setDiskWriteResult(false); return; } XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str); FileUtils.sync(str); str.close(); ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);看看第一行代码 :FileOutputStream str = createFileOutputStream(mFile); 还记得一开始让你注意的那个文件吗?现在是不是终于用的了mFile。OK,其实就是把数据写在了这个文件里面了。
XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str); mcr.mapToWriteToDisk这个数据是那里来的呢,其实 mapToWriteToDisk这个数据就是我们刚才说的那个存储数据的那个HashMap。看看如下的代码就知道了:
private MemoryCommitResult commitToMemory() { MemoryCommitResult mcr = new MemoryCommitResult(); synchronized (SharedPreferencesImpl.this) { // We optimistically don't make a deep copy until // a memory commit comes in when we're already // writing to disk. if (mDiskWritesInFlight > 0) { // We can't modify our mMap as a currently // in-flight write owns it. Clone it before // modifying it. // noinspection unchecked mMap = new HashMap<String, Object>(mMap); } mcr.mapToWriteToDisk = mMap;
在这个函数commitToMemory里紧接着就有如下代码
for (Map.Entry<String, Object> e : mModified.entrySet()) { String k = e.getKey(); Object v = e.getValue(); if (v == this) { // magic value for a removal mutation if (!mMap.containsKey(k)) { continue; } mMap.remove(k); } else { boolean isSame = false; if (mMap.containsKey(k)) { Object existingValue = mMap.get(k); if (existingValue != null && existingValue.equals(v)) { continue; } } mMap.put(k, v); }
看到了吗,这样一来原本暂时存储数据的mModified就把数据传出去了给了mapToWriteToDisk 这下明白了吧。实际上SharedPreferences的原理就是先把数据存储在hashmap,然后再写入一个文件里面。那么写入的这个文件是什么文件呢?文件名是什么呢?和一个APP有什么关系呢;还有上面说的“这说明一个特定的preferences,其实是共多个客户共享的”这里的多个客户共享是指什么呢?欲知详情,请看下面分解:
一般情况下,我们是通过如下的代码来获取一个SharedPreferences:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
(1)这里的this当然就是指当前Activity或是Service的Context,我们知道其实Activity和Service其实本质都是一样的,他们都是继承Context而来的。
(2)这里我们重点看这个函数getDefaultSharedPreferences(Context context)在PreferenceManager这个CLASS里面是这样定义的:
public static SharedPreferences getDefaultSharedPreferences(Context context) { return context.getSharedPreferences(getDefaultSharedPreferencesName(context), getDefaultSharedPreferencesMode()); }看了这个函数,你一定豁然开朗了,getDefaultSharedPreferences是一个static 函数,实际上就是通过这个context来获取的SharedPreferences。首先我们来看看getDefaultSharedPreferencesName(context)这个函数传递的是什么参数:
private static String getDefaultSharedPreferencesName(Context context) { return context.getPackageName() + "_preferences"; }
原来是传递了一个字符串,而且这个字符串是由当前APP的PackageName+_preferences组成。现在是否看出点什么呢? 我们知道一个APP可以有大量的activity和Service,他们的PackageName是一样的,难道一个APP所有的activity和Service都传递相同的一个字符串去获取一个SharedPreferences吗? 难道说一个一个APP所有的activity和Service都共享一个SharedPreferences? 是这样吗?我们继续往下看:
所以我们最终来关注context的getSharedPreferences函数的实现了。
我们知道Context.java里面只是Context定义了接口,真正实现这个接口的其实是在这里ContextImpl.java,所以我们很快找到了这个函数的定义:
public SharedPreferences getSharedPreferences(String name, int mode) { SharedPreferencesImpl sp; synchronized (sSharedPrefs) { sp = sSharedPrefs.get(name); if (sp == null) { File prefsFile = getSharedPrefsFile(name); sp = new SharedPreferencesImpl(prefsFile, mode); sSharedPrefs.put(name, sp); return sp; } } if ((mode & Context.MODE_MULTI_PROCESS) != 0 || getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) { // If somebody else (some other process) changed the prefs // file behind our back, we reload it. This has been the // historical (if undocumented) behavior. sp.startReloadIfChangedUnexpectedly(); } return sp; }getSharedPreferences这个函数很简单:
(1)首先是通过传递进来的name来获取一个SharedPreferencesImpl实例,SharedPreferencesImpl已经在上面说过了,他其实就是一个SharedPreferences。这里是通过sSharedPrefs.get(name);这段代码来获取的,看一下sSharedPrefs这个量的定义你就会吓一跳:
private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs = new HashMap<String, SharedPreferencesImpl>();
sSharedPrefs是一个私有的、最重要的是:还是一个静态的、并且还是一个final型的HashMap<String, SharedPreferencesImpl>的变量。这里最重要的要理解static:这表示sSharedPrefs是所有Context实例共享的,也就是说,原则上所有APP如此当前的存在可用的他们的SharedPreferences都是暂时存在这里的。那么如果当前需要用的SharedPreferences在sSharedPrefs不存在,那怎么办? 当然是创建一个新的SharedPreferences,然后再加入到sSharedPrefs看看上面的这个函数: getSharedPreferences的实现就知道:
if (sp == null) { File prefsFile = getSharedPrefsFile(name); sp = new SharedPreferencesImpl(prefsFile, mode); sSharedPrefs.put(name, sp); return sp; }
(1)上面我贴出来了创建一个新的SharedPreferences的代码。可以看出:先是通过name创建一个File 。我们来来看看到底是创建了什么文件:public File getSharedPrefsFile(String name) { return makeFilename(getPreferencesDir(), name + ".xml"); }
看到了吧,这下就明白了,其实就是创建了一个.xml。而文件名其实就是这个name。回顾一下name其实就是这个APP的PackageName+_preferences组成。
(2)XML文件创建好了,接着sp = new SharedPreferencesImpl(prefsFile, mode);就创建了一个SharedPreferencesImpl实例。 我们回顾一下:在开始介绍SharedPreferencesImpl的实现的时候就说到了他的构造函数,这个构成函数的一个参数就是file,一个文件。 这下就知道了 ,这个文件其实就是一个XML文件。
所以:对于一个APP的SharedPreferences “all clients share”的意思就是:一个APP的所有clients share这个SharedPreferences 。而这里的 clients显然是指这个APP的所有activity和Service。
- Activity之SharedPreferences探究
- Android初级之路---------探究Activity
- 探究Activity
- 【android-tips】Activity间数据传递之Bundle和SharedPreferences
- Android Activity SharedPreferences
- activity启动模式探究
- Android Activity活动探究
- Activity生命周期的探究
- Activity生命周期探究
- Activity的探究
- activity启动模式探究
- activity启动模式探究
- 探究活动-Activity
- Android Activity探究活动
- 继续探究缓存框架ACache和SharedPreferences
- 多个Activity 共享SharedPreferences
- 不是Activity能用sharedPreferences吗?
- Android Sample Code之API Demos (Activity三):Persistent State 《保存数据SharedPreferences》
- 直接选择排序
- oracle在linux下开机自动启动的配置
- 银联IC卡读卡流程详解--读卡器与卡交互指令
- mysql 操作语句_select子句、数据库的操作
- QML父窗口半透明情况下子窗口不透明
- Activity之SharedPreferences探究
- ubuntu12.04 默认以命令行启动
- latex 简历 英文模板
- 【Android自动化打包】03. APK的数字签名
- 去掉HTML标签
- 在游戏中如何让随机更加智能
- 开源 免费 java CMS - FreeCMS1.6 敏感词管理
- 搞IT的技术人员为什么会如此苦逼
- Bad JNI version passed to AttachCurrentThread: