文章出处:http://blog.csdn.net/shift_wwx
请转载的朋友标明出处~~
前言:之前 SharedPreference 实现不同进程间的数据共享 中谈了一下数据共享,最近在用到这一块的时候感觉有点模糊,索性就看一下source code,分享一下~
一、使用
分两个函数:
private void getDataFromSP() {
SharedPreferences sp = getSharedPreferences("sp_name", Context.MODE_PRIVATE);
int value1 = sp.getInt("field_1", 0);
String value2 = sp.getString("field_2", "");
}
private void setDataToSp() {
SharedPreferences sp = getSharedPreferences("sp_name", Context.MODE_PRIVATE);
Editor editor = sp.edit();
editor.putInt("field_1", 0);
editor.putString("field_2", "");
editor.commit();
}
二、source code 详解
1、获取对象
code 中拖过getSharePreferences 来获取SharedPreferences 的对象,调用过程不说了,说一下实现:
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
if (sSharedPrefs == null) {
sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
}
final String packageName = getPackageName();
ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
if (packagePrefs == null) {
packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
sSharedPrefs.put(packageName, packagePrefs);
}
// At least one application in the world actually passes in a null
// name. This happened to work because when we generated the file name
// we would stringify it to "null.xml". Nice.
if (mPackageInfo.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT) {
if (name == null) {
name = "null";
}
}
sp = packagePrefs.get(name);
if (sp == null) {
File prefsFile = getSharedPrefsFile(name);
sp = new SharedPreferencesImpl(prefsFile, mode);
packagePrefs.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;
}
通过code可以看到几点:
1)一个package 可以创建多个xml,每个xml代表一个SharedPreferences 对象
2)如果xml 没有的时候会新建文件,新建一个SharedPreferences 对象
3)参数mode是 Context.MODE_MULTI_PROCESS 的时候,可以允许多进程操作
4)SharedPreferences 真正实现的class 是SharedPreferencesImpl
2、读取信息
SharedPreferences 从source code看,是一个interface:
public interface SharedPreferences {}
所以是需要实现的,实现它的地方之前讲过是 SharedPreferencesImpl.java
几个get 的方法:
public int getInt(String key, int defValue) {
synchronized (this) {
awaitLoadedLocked();
Integer v = (Integer)mMap.get(key);
return v != null ? v : defValue;
}
}
public Set<String> getStringSet(String key, Set<String> defValues) {
synchronized (this) {
awaitLoadedLocked();
Set<String> v = (Set<String>) mMap.get(key);
return v != null ? v : defValues;
}
}
public boolean getBoolean(String key, boolean defValue) {
synchronized (this) {
awaitLoadedLocked();
Boolean v = (Boolean)mMap.get(key);
return v != null ? v : defValue;
}
}
从code 看,是在SharedPreferencesImpl 中有个mMap的东西:
private Map<String, Object> mMap;
step 1 中讲过在getSharedPreferences的时候会新建一个SharedPreferencesImpl 对象:
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
startLoadFromDisk();
}
private void startLoadFromDisk() {
synchronized (this) {
mLoaded = false;
}
new Thread("SharedPreferencesImpl-load") {
public void run() {
synchronized (SharedPreferencesImpl.this) {
loadFromDiskLocked();
}
}
}.start();
}
最终:
map = XmlUtils.readMapXml(str);
3、存储数据
使用的时候SharedPreferences 用内部类Editor,可是Source code 中Editor 也是interface ,实现的地方是EditorImpl
public final class EditorImpl implements Editor {
private final Map<String, Object> mModified = Maps.newHashMap();
private boolean mClear = false;
......
......
}
可以看到会新建一个Map,所以,在使用的时候一直put,就是存进这个map 中。
几个put 的接口:
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;
}
}
public Editor putLong(String key, long value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
public Editor putFloat(String key, float value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
public Editor putBoolean(String key, boolean value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
put 完后最终调用commit 来完成存储:
public boolean commit() {
MemoryCommitResult mcr = commitToMemory();
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
}
notifyListeners(mcr);
return mcr.writeToDiskResult;
}
存储过程暂时不介绍,注意的是最后的Listener:
private void notifyListeners(final MemoryCommitResult mcr) {
if (mcr.listeners == null || mcr.keysModified == null ||
mcr.keysModified.size() == 0) {
return;
}
if (Looper.myLooper() == Looper.getMainLooper()) {
for (int i = mcr.keysModified.size() - 1; i >= 0; i--) {
final String key = mcr.keysModified.get(i);
for (OnSharedPreferenceChangeListener listener : mcr.listeners) {
if (listener != null) {
listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, key);
}
}
}
} else {
// Run this function on the main thread.
ActivityThread.sMainThreadHandler.post(new Runnable() {
public void run() {
notifyListeners(mcr);
}
});
}
}
onSharedPreferenceChanged 是肯定要在主线程中调用的。
4、Editor 其他的一些接口
1)clear
public Editor clear() {
synchronized (this) {
mClear = true;
return this;
}
}
在commit 的时候会判断 mClear:
if (mClear) {
if (!mMap.isEmpty()) {
mcr.changesMade = true;
mMap.clear();
}
mClear = false;
......
......
}
2)remove
public Editor remove(String key) {
synchronized (this) {
mModified.put(key, this);
return this;
}
}
刚开始看到这个很奇怪,功能是remove的,为什么会put呢,因为是两份map操作,这个临时的map,即mModified不知道mMap 里面有没有这个,所以,就put 了this。
在commit的时候会比较每个元素,当碰到了this 之后会确认mMap 里面是否存在,没有就算了,有就remove掉:
if (v == this || v == null) {
if (!mMap.containsKey(k)) {
continue;
}
mMap.remove(k);
}
3)apply
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);
}
其实完成和commit 的功能差不多。
两者的区别:
/**
* Commit your preferences changes back from this Editor to the
* {@link SharedPreferences} object it is editing. This atomically
* performs the requested modifications, replacing whatever is currently
* in the SharedPreferences.
*
* <p>Note that when two editors are modifying preferences at the same
* time, the last one to call apply wins.
*
* <p>Unlike {@link #commit}, which writes its preferences out
* to persistent storage synchronously, {@link #apply}
* commits its changes to the in-memory
* {@link SharedPreferences} immediately but starts an
* asynchronous commit to disk and you won't be notified of
* any failures. If another editor on this
* {@link SharedPreferences} does a regular {@link #commit}
* while a {@link #apply} is still outstanding, the
* {@link #commit} will block until all async commits are
* completed as well as the commit itself.
*
* <p>As {@link SharedPreferences} instances are singletons within
* a process, it's safe to replace any instance of {@link #commit} with
* {@link #apply} if you were already ignoring the return value.
*
* <p>You don't need to worry about Android component
* lifecycles and their interaction with <code>apply()</code>
* writing to disk. The framework makes sure in-flight disk
* writes from <code>apply()</code> complete before switching
* states.
*
* <p class='note'>The SharedPreferences.Editor interface
* isn't expected to be implemented directly. However, if you
* previously did implement it and are now getting errors
* about missing <code>apply()</code>, you can simply call
* {@link #commit} from <code>apply()</code>.
*/
区别1:
commit 是有返回值的,表示是否成功;而apply是没有的。
由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要用commit的。
区别2:
commit 同步提交到disk 中;而apply 是立马存在内存中,然后异步去存储到disk中,中间是收不到失败的信息的。
不用去担心线程安全问题, 因为如果一个其他的线程去commit,而刚好有一个还没有完成的apply,commit会被阻塞到异步线程提交完成。