先说说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()这些函数就知道,
对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。
转载于:https://blog.51cto.com/6747181/1380408