Android SharedPreferences源码分析
概述
SharedPreferences 是一种轻量级的本地存储,以键值对形式将数据持久化存储到 XML 文件中,存储在应用的私有目录 /data/data/<package>/shared_prefs 目录下,主要用于存储简单的配置信息。
基本用法
// 获取 SharedPreferences 实例
val prefs = getSharedPreferences("my_prefs", MODE_PRIVATE)
// 写入数据
val editor = prefs.edit()
editor.putString("name", "小明")
editor.putInt("age", 18)
editor.commit() // 同步,有返回值
editor.apply() // 异步,无返回值
// 读取数据
val name = prefs.getString("name", "default")!!
val age = prefs.getInt("age", 0)
Log.e("TAG", "name: $name")
Log.e("TAG", "age: $age")
源码分析
SharedPreferencesImpl属性
final class SharedPreferencesImpl implements SharedPreferences {
private final File mFile; // xml文件
private final File mBackupFile; // 备份文件
private final int mMode; // 操作模式
private final Object mLock = new Object(); // 读写锁
private final Object mWritingToDiskLock = new Object(); // 写入锁
private Map<String, Object> mMap; // 缓存xml的数据
private boolean mLoaded = false; // 数据是否已加载
}
获取SP实例
ContextImpl.getSharedPreferences()
public SharedPreferences getSharedPreferences(String name, int mode) {
File file;
synchronized (ContextImpl.class) {
if (mSharedPrefsPaths == null) {
mSharedPrefsPaths = new ArrayMap<>();
}
file = mSharedPrefsPaths.get(name);
if (file == null) {
file = getSharedPreferencesPath(name);
mSharedPrefsPaths.put(name, file);
}
}
// 获取sp实例
return getSharedPreferences(file, mode);
}
public SharedPreferences getSharedPreferences(File file, int mode) {
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
sp = cache.get(file);
if (sp == null) {
checkMode(mode);
// 创建sp实例
sp = new SharedPreferencesImpl(file, mode);
cache.put(file, sp);
return sp;
}
}
return sp;
}
SharedPreferencesImpl构造函数
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
mThrowable = null;
// 从磁盘加载数据
startLoadFromDisk();
}
SharedPreferecnesImpl.startLoadFromDisk()
private void startLoadFromDisk() {
synchronized (mLock) {
mLoaded = false;
}
// 开启线程加载数据
sLoadExecutor.execute(() -> {
loadFromDisk();
});
}
SharedPreferecnesImpl.loadFromDisk()
private void loadFromDisk() {
synchronized (mLock) {
// 如果已读取则直接返回
if (mLoaded) {
return;
}
}
Map<String, Object> map = null;
StructStat stat = null;
Throwable thrown = null;
try {
stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
// 加载sp文件,整体加载,解析为Map对象
str = new BufferedInputStream(
new FileInputStream(mFile), 16 * 1024);
map = (Map<String, Object>) XmlUtils.readMapXml(str);
} catch (Exception e) {
} finally {
IoUtils.closeQuietly(str);
}
}
} catch (ErrnoException e) {
} catch (Throwable t) {
thrown = t;
}
synchronized (mLock) {
// 标记为已加载
mLoaded = true;
mThrowable = thrown;
try {
if (thrown == null) {
if (map != null) {
// 缓存数据
mMap = map;
mStatTimestamp = stat.st_mtim;
mStatSize = stat.st_size;
} else {
mMap = new HashMap<>();
}
}
} catch (Throwable t) {
mThrowable = t;
} finally {
mLock.notifyAll();
}
}
}
获取数据
SharedPreferecnesImpl.getString()
public String getString(String key, @Nullable String defValue) {
synchronized (mLock) {
// 等待数据加载完成,可能阻塞当前线程
awaitLoadedLocked();
// 从Map缓存中获取数据
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
private void awaitLoadedLocked() {
if (!mLoaded) {
BlockGuard.getThreadPolicy().onReadFromDisk();
}
while (!mLoaded) {
try {
mLock.wait();
} catch (InterruptedException unused) {
}
}
if (mThrowable != null) {
throw new IllegalStateException(mThrowable);
}
}
设置数据
EditorImpl.putString()
public final class EditorImpl implements Editor {
private final Object mEditorLock = new Object();
// 缓存修改数据,用于批量处理
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;
}
}
}
EditorImpl.commit()
public boolean commit() {
// 先修改内存缓存的数据
MemoryCommitResult mcr = commitToMemory();
// 再处理磁盘缓存的数据
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, null);
try {
// 等待写入完成
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
} finally {
}
notifyListeners(mcr);
// 返回值
return mcr.writeToDiskResult;
}
SharedPreferencesImpl.commitToMemory()
private MemoryCommitResult commitToMemory() {
long memoryStateGeneration;
boolean keysCleared = false;
List<String> keysModified = null;
Set<OnSharedPreferenceChangeListener> listeners = null;
Map<String, Object> mapToWriteToDisk;
synchronized (SharedPreferencesImpl.this.mLock) {
if (mDiskWritesInFlight > 0) {
mMap = new HashMap<String, Object>(mMap);
}
// 将Map赋值给mapToWriteToDisk对象
mapToWriteToDisk = mMap;
mDiskWritesInFlight++;
boolean hasListeners = mListeners.size() > 0;
if (hasListeners) {
keysModified = new ArrayList<String>();
listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
}
synchronized (mEditorLock) {
boolean changesMade = false;
if (mClear) {
if (!mapToWriteToDisk.isEmpty()) {
changesMade = true;
mapToWriteToDisk.clear();
}
keysCleared = true;
mClear = false;
}
// 批量修改内存的Map数据
for (Map.Entry<String, Object> e : mModified.entrySet()) {
String k = e.getKey();
Object v = e.getValue();
if (v == this || v == null) {
if (!mapToWriteToDisk.containsKey(k)) {
continue;
}
mapToWriteToDisk.remove(k);
} else {
if (mapToWriteToDisk.containsKey(k)) {
Object existingValue = mapToWriteToDisk.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
mapToWriteToDisk.put(k, v);
}
changesMade = true;
if (hasListeners) {
keysModified.add(k);
}
}
mModified.clear();
if (changesMade) {
mCurrentMemoryStateGeneration++;
}
memoryStateGeneration = mCurrentMemoryStateGeneration;
}
}
return new MemoryCommitResult(memoryStateGeneration, keysCleared, keysModified,
listeners, mapToWriteToDisk);
}
SharedPreferencesImpl.enqueueDiskWrite()
private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {
// 如果postWriteRunnable为null,则表示同步提交,否则表示异步提交
final boolean isFromSyncCommit = (postWriteRunnable == null);
final Runnable writeToDiskRunnable = new Runnable() {
@Override
public void run() {
synchronized (mWritingToDiskLock) {
// 将缓存写入磁盘,全量写入
writeToFile(mcr, isFromSyncCommit);
}
synchronized (mLock) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
// 同步提交执行逻辑:
if (isFromSyncCommit) {
boolean wasEmpty = false;
synchronized (mLock) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
writeToDiskRunnable.run();
return;
}
}
// 异步提交执行逻辑:
// 将写入任务添加到QueuedWork队列中
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}
EditorImpl.apply()
public void apply() {
// 先修改内存缓存数据
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
@Override
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
}
};
QueuedWork.addFinisher(awaitCommit);
Runnable postWriteRunnable = new Runnable() {
@Override
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);
}
};
// 调用enqueueDiskWrite(),最终提交到队列中
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
notifyListeners(mcr);
}
缺点
ANR问题
- commit():同步写入磁盘,容易主线程阻塞。
- apply():虽然是异步,但是系统在 Activity.onStop() 执行前会等待所有apply() 执行完。
- 数据量多时会产生 ANR。
public void handleStopActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
final StopInfo stopInfo = new StopInfo();
performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest,
reason);
if (localLOGV) Slog.v(
TAG, "Finishing stop of " + r + ": win=" + r.window);
updateVisibility(r, false);
// Make sure any pending writes are now committed.
if (!r.isPreHoneycomb()) {
// 会等待所有的sp.apply()执行完毕
QueuedWork.waitToFinish();
}
stopInfo.setActivity(r);
stopInfo.setState(r.state);
stopInfo.setPersistentState(r.persistentState);
if (android.companion.Flags.enableTaskContinuity()) {
stopInfo.setHandoffActivityData(r.handoffActivityData);
}
pendingActions.setStopInfo(stopInfo);
mSomeActivitiesChanged = true;
}
全量写入
- 每次修改都需要将整个 XML 重新写入磁盘。
- 数据大时,写入较慢。
线程不安全
- 每次调用 edit() 都会创建一个新的 Editor 对象。
- 多线程情况下不安全。
整体加载
- 第一次加载是全量写入内存,并常驻内存。
- 数据量大时,内存占用较高。


本文详细解读了AndroidSharedPreferences的源码,包括其内存模型、初始化、读写数据流程以及内存优化策略,指出commit()同步提交可能导致主线程堵塞引发ANR,推荐使用apply()进行异步提交以提高性能。
2607

被折叠的 条评论
为什么被折叠?



