正气清白,流于乾坤
SharedPreferences是Android开发中经常使用的一种轻量级存储方式。本着『知其然知其所以然』的宗旨,我们来探讨一下SharedPreferences的实现过程。
1. 基本知识
- SharedPreferences是以Key-Value(键值对)的形式进行存储的;
- SharedPreferences最终存储在xml文件上;
- SharedPreferences是线程安全的,但不是进程安全的(MODE_MULTI_PROCESS是个鸡肋。)
2. 基本框架
SharedPreferences是一个接口,其内部有包含除了用于获取数据的方法外,还有
- 用于存储,删除和提交数据的Editor接口;
- 用于监听的SharedPreferences变化的OnSharedPreferenceChangeListener接口
public interface SharedPerferences{
public interface OnSharedPreferenceChangeListener {
/**
* Called when a shared preference is changed, added, or removed.
* (当SharedPreferences更新,添加,删除时被调用)
* This callback will be run on your main thread.(此回调运行在主线程)
*/
void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key);
}
public interface Editor{
// 存储操作
Editor putXXX(String key, XXX value);
// 删除操作
Editor remove(String key);
Editor clear();
// 用于提交的commit()和 apply()方法
boolean commit();
void apply();
}
···
Map<String, ?> getAll();
// 获取数据
XXX getXXX(String key, XXX defValue);
boolean contains(String key);
// 返回给我们一个Editor,用于执行其存储,更新和提交操作。
Editor edit();
}
通过上面的代码我们可以看到SharePerferences和Editor都是接口,主要的核心实现逻辑都在其实现类中。
final class SharedPreferencesImpl implements SharedPreferences {
private final File mFile;
private final File mBackupFile;
private final int mMode;
private final Object mLock = new Object();
private final Object mWritingToDiskLock = new Object();
@GuardedBy("mLock")
private Map<String, Object> mMap;
@Override
@Nullable
public String getString(String key, @Nullable String defValue) {
synchronized (mLock) {
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
@Override
public Editor edit() {
synchronized (mLock) {
awaitLoadedLocked();
}
return new EditorImpl();
}
public final class EditorImpl implements Editor {
private final Object mEditorLock = new Object();
// 通过putXXX()方法,可以看出值都是先保存在mModified里面
@GuardedBy("mEditorLock")
private final Map<String, Object> mModified = new HashMap<>();
@Override
public Editor putString(String key, @Nullable String value) {
synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
@Override
public Editor remove(String key) {
synchronized (mEditorLock) {
mModified.put(key, this);
return this;
}
}
@Override
public void apply() {
// 大体流程跟commit()一样,后文讲解
}
@Override
public boolean commit() {
// 注意这个commitToMemory()方法。以及MemoryCommitResult类
final MemoryCommitResult mcr = commitToMemory();
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
notifyListeners(mcr);
return mcr.writeToDiskResult;
}
}
// 内部类MemoryCommitResult
private static class MemoryCommitResult {
@Nullable final Set<OnSharedPreferenceChangeListener> listeners;
// 注意这个Map
final Map<String, Object> mapToWriteToDisk;
// 这里的CountDownLatch保证线程的执行顺序。
final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);
@GuardedBy("mWritingToDiskLock")
volatile boolean writeToDiskResult = false;
boolean wasWritten = false;
void setDiskWriteResult(boolean wasWritten, boolean result) {
this.wasWritten = wasWritten;
writeToDiskResult = result;
writtenToDiskLatch.countDown();
}
}
}
通过查看源码,我们会发现数据基本都在三个HashMap中进行流转,如下图所示:
3. 基本操作
- 获取数据getXXX():
从SharedPreferencesImpl的mMap中获取数据;
- 存储数据putXXX():
- 首先我们需要通过edit()获取EditorImpl对象;
- 通过putXXX()方法将数据存入到EditorImpl的mModified变量中。
- 提交操作commit()或apply():
- 通过commitToMemory()方法将数据同步SharedPreferencesImpl的mMap中,然后将EditorImpl中的mModified的数据转移到MemoryCommitResult的mapToWriteToDisk变量中。
- 调用enqueueDiskWrite()方法,将MemoryCommitResult的mapToWriteToDisk写入到XML文件中。
4. 总结
这里将SharedPreferences的整体流程给梳理出来,下一篇文章将深入到源码的细节去进一步分析。