SharedPreference 是 Android 平台提供的一种轻量 级的数据存储 方式,它用于存储 应用的配置信息或者一些简单 的数据 。SharedPreference 基于键值对的存储,并且支持基本的数据类型,如整型、字符串、布尔值等。它的使用非常简单方便,适合保存 一些小量 的数据。
ANR(Application Not Responding) 指的是应用程序无法在规定的时间内响应用户输入事件,导致应用失去响应 并无法继续 正常运行 。通常 情况下,ANR 出现在主线程 中执行耗时操作 或者发生死锁 的情况下,比如网络请求长时间没有响应、数据库操作耗时等。当 ANR 发生时 ,系统 会弹 出一个错误对话框 ,告知用户应用程序无响应,用户 可以选择等待或者关闭应用。
SharedPreference 有两种提交方式:commit (同步) 和 apply(异步)
我相信应该很少人会使用 commit,因为 SharedPreference 的提交涉及 到读写文件,是耗时操作,所以如果放在主线程的话很有可能会导致 ANR
但是,你以为都使用 apply(异步)就完事大吉了吗,并不然,滥用 SharedPreference 的 apply(异步)也有可能会导致 ANR 的
apply() 的源码:
java
package android.app;
final class SharedPreferencesImpl implements SharedPreferences {
public final class EditorImpl implements Editor {
@Override
public void apply() {
// 将所有事务整理成 MemoryCommitResult 对象
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
@Override
public void run() {
try {
// 阻塞当前线程 (UI线程)
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
}
};
QueuedWork.addFinisher(awaitCommit); // 保存到一个静态的数据结构
Runnable postWriteRunnable = new Runnable() {
@Override
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit); // 从静态的数据结构中移除
}
};
// 异步执行写操作
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
// ....
}
}
}
在调用 apply() 之后,首先会将所有提交的事务整理成一个对象(mcr ),然后定义了一个 awaitCommit(Runnable),顾名思义就是等待提交,这个 Runnable 是一个 CountDownLatch 的 await(),作用是阻塞当前线程。
之后把 awaitCommit 加入 到一个静态 的数据结构(等会说)
下面定义了一个 postWriteRunnable(Runnable),哎里面是执行上面的 awaitCommit(Runnable)和移除静态数据结构的 awaitCommit。
这样子设计是因为 Android 系统为了保障在页面切换,也就是在多进程中 sp 文件能够存储成功,在 ActivityThread 的 handlePauseActivity 和 handleStopActivity 时会通过 waitToFinish 保证 这些异步任务都 已经被执行完成。
java
package android.app;
public final class ActivityThread extends ClientTransactionHandler
implements ActivityThreadInternal {
// ....
@Override
public void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving,
int configChanges, PendingTransactionActions pendingActions, String reason) {
// Make sure any pending writes are now committed.
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
// ....
}
}
OK 下面的 SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable); 就是把写任务放在子线程中去执行。
总结为什么滥用 SharedPreference 会导致 ANR 问题呢?
① commit 方法读写耗时操作放在主线程执行
② apply 方法主线程阻塞等待子线程读写执行完
SharedPreference 现在其实已经非常不建议去使用了,因为它是全量更新 ,所以保存的数据越多 ,所需要的耗时 就越 久,越容易发生 ANR。这个时候需要有替代品(MMKV)