通过问题的形式来分析SharedPreferences的源码
1.硬盘数据怎么加载到内存的?
- 构造方法执行加载
- 在子线程中加载,因此加载过程不会阻塞 UI
- 本地有数据, 将本地数据 赋值给内存的 mMap,没有的话初始化mMap
final class SharedPreferencesImpl implements SharedPreferences {
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
mThrowable = null;
//1.构造方法执行加载
startLoadFromDisk();
}
private void startLoadFromDisk() {
synchronized (mLock) {
mLoaded = false;
}
//2.在子线程中加载,因此加载过程不会阻塞 UI
new Thread("SharedPreferencesImpl-load") {
public void run() {
//这个执行一次
loadFromDisk();
}
}.start();
}
private void loadFromDisk() {
//...
Map<String, Object> map = null;
StructStat stat = null;
Throwable thrown = null;
try {
stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
str = new BufferedInputStream(
new FileInputStream(mFile), 16 * 1024);
// 从本地影片的 xml 加载数据,然后xml 数据转为 map
map = (Map<String, Object>) XmlUtils.readMapXml(str);
} catch (Exception e) {
Log.w(TAG, "Cannot read " mFile.getAbsolutePath(), e);
} finally {
IoUtils.closeQuietly(str);
}
}
} catch (ErrnoException e) {
} catch (Throwable t) {
}
synchronized (mLock) {
mLoaded = true;
mThrowable = thrown;
try {
if (thrown == null) {
// 3.本地有数据, 将本地数据 赋值给内存的 mMap
if (map != null) {
mMap = map;
mStatTimestamp = stat.st_mtim;
mStatSize = stat.st_size;
} else {
mMap = new HashMap<>();
}
}
} catch (Throwable t) {
}
}
}
}
2.getString() 流程是怎样的?
1.获取 SP 的值的时候 若当前文件过大在加载过程中.则 UI 阻塞
2.在获取 sp 值的时候 会一直判断标记mLoaded,变为 true,true表示从本地加载到内存成功,为false 则 UI 阻塞
3.数据加载成功,若 UI 线程阻塞则唤醒
4.使用 wait/notify 机制
代码语言:javascript复制final class SharedPreferencesImpl implements SharedPreferences {
private boolean mLoaded = false;
SharedPreferencesImpl(File file, int mode) {
//2.在获取 sp 值的时候 会一直判断标记mLoaded,变为 true 则表示是否从本地加载到内存成功
// 为 false 则 UI 阻塞
mLoaded = false;
}
@Override
@Nullable
public String getString(String key, @Nullable String defValue) {
synchronized (mLock) {
//1.获取 SP 的值的时候 若当前文件过大在加载过程中.则 UI 阻塞
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
//3.数据加载成功,若 UI 线程阻塞则唤醒
@GuardedBy("mLock")
private void awaitLoadedLocked() {
//...
while (!mLoaded) {
try {
//4.使用 wait/notify 机制
// 这儿阻塞 UI ,等待加载成功会唤醒
mLock.wait();
} catch (InterruptedException unused) {
}
}
if (mThrowable != null) {
throw new IllegalStateException(mThrowable);
}
}
}
3.数据怎么存储到本地的
这个过程涉及到 3 个 map 之间的数据传递
1. map : mModified
接受用户数据的 map
代码语言:javascript复制private final Map<String, Object> mModified = new HashMap<>();
2. map : mapToWriteToDisk
存储到硬盘使用的 map
代码语言:javascript复制final Map<String, Object> mapToWriteToDisk;
3. map: mMap
内存中存储数据的 map,初次加载也是将本地xml数据放入 mMap
代码语言:javascript复制private Map<String, Object> mMap;
数据传递的过程:
1.在 9.0之前的是: mModified 将数据提交到内存 mMap ,然后 clear()清空自己
2.在 9.0的处理是: mModified 将数据直接提交给mapToWriteToDisk
3.mapToWriteToDisk 将数据 存储到本地
4. commitToMemory() 存储到内存做了什么?
1.使用了两个同步代码块,两个对象锁
2.执行writing到本地的时候,写入过程尚未完成时,又调用了 commitToMemory(),而 且此时没有执行mDiskWritesInFlight--,直接修改 mMap 可能会影响写入结果
3. 数据提交到mapToWriteToDisk
代码语言:javascript复制final class SharedPreferencesImpl implements SharedPreferences {
public final class EditorImpl implements Editor {
private MemoryCommitResult commitToMemory() {
long memoryStateGeneration;
List<String> keysModified = null;
Map<String, Object> mapToWriteToDisk;
//对象锁啊,和get操作同步
synchronized (SharedPreferencesImpl.this.mLock) {
if (mDiskWritesInFlight > 0) {
//深copy
mMap = new HashMap<String, Object>(mMap);
}
//将mMap赋值给mapToWriteToDisk,写入本地 xml 使用
mapToWriteToDisk = mMap;
mDiskWritesInFlight ;
//...
//对象锁啊 和put 操作同步
synchronized (mEditorLock) {
boolean changesMade = false;
//...
// 变量我们的mModified map 里面放入的是将要 存储内存的 sp 数据
for (Map.Entry<String, Object> e : mModified.entrySet()) {
String k = e.getKey();
Object v = e.getValue();
if (v == this || v == null) {
//...
} else {
//已存在
if (mapToWriteToDisk.containsKey(k)) {
Object existingValue = mapToWriteToDisk.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
// 放入 map
mapToWriteToDisk.put(k, v);
}
changesMade = true;
//...
}
// 每次内存存储完 清空一下mModified类型的hashMap 的数据
mModified.clear();
if (changesMade) {
mCurrentMemoryStateGeneration ;
}
memoryStateGeneration = mCurrentMemoryStateGeneration;
}
}
return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
mapToWriteToDisk);
}
}
}
其他:
synchronized 的详解
深copy
见相关文章