没有合适的资源?快使用搜索试试~ 我知道了~
Android中DataStore替代SharedPreferences
需积分: 5 1 下载量 177 浏览量
2022-04-01
09:54:58
上传
评论
收藏 1.1MB PDF 举报
温馨提示
试读
14页
一 、DataStore介绍 Jetpack DataStore 是一种改进的新数据存储解决方案,允许使用协议缓冲区存储键值对或类型化对 象。 DataStore 以异步、一致的事务方式存储数据,克服了 SharedPreferences(以下统称为SP)的 一些缺点。 DataStore 基于 Kotlin 协程和 Flow 实现,并且可以对 SP 数据进行迁移,旨在取代 SP 。 DataStore 提供了两种不同的实现: Preferences DataStore 与 Proto DataStore ,其中 Preferences DataStore 用于存储键值对; Proto DataStore 用于存储类型化对象,后面会分别给出 对应的使用例子。 二、SharedPreferences缺点 DataStore 出现之前,我们用的最多的存储方式毫无疑问是 SP ,其使用方式简单、易用,广受好评。 然而 google 对 SP 的定义为轻量级存储,如果存储的数据少,使用起来没有任何问题,当需要存储数据 比较多时, SP 可能会导致以下问题:
资源详情
资源评论
资源推荐
一 、DataStore介绍
Jetpack DataStore 是一种改进的新数据存储解决方案,允许使用协议缓冲区存储键值对或类型化对
象。 DataStore 以异步、一致的事务方式存储数据,克服了 SharedPreferences(以下统称为SP)的
一些缺点。 DataStore 基于 Kotlin 协程和 Flow 实现,并且可以对 SP 数据进行迁移,旨在取代 SP 。
DataStore 提供了两种不同的实现: Preferences DataStore 与 Proto DataStore ,其中
Preferences DataStore 用于存储键值对; Proto DataStore 用于存储类型化对象,后面会分别给出
对应的使用例子。
二、SharedPreferences缺点
DataStore 出现之前,我们用的最多的存储方式毫无疑问是 SP ,其使用方式简单、易用,广受好评。
然而 google 对 SP 的定义为轻量级存储,如果存储的数据少,使用起来没有任何问题,当需要存储数据
比较多时, SP 可能会导致以下问题:
SP第一次加载数据时需要全量加载,当数据量大时可能会阻塞UI线程造成卡顿
SP读写文件不是类型安全的,且没有发出错误信号的机制,缺少事务性API
commit() / apply()操作可能会造成ANR问题:
commit() 是同步提交,会在 UI 主线程中直接执行 IO 操作,当写入操作耗时比较长时就会导致 UI 线程
被阻塞,进而产生 ANR ; apply() 虽然是异步提交,但异步写入磁盘时,如果执行了 Activity /
Service 中的 onStop() 方法,那么一样会同步等待 SP 写入完毕,等待时间过长时也会引起 ANR 问题。
针对 apply() 我们展开来看一下:
SharedPreferencesImpl#EditorImpl.java中最终执行了 apply() 函数:
public final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);
public void apply() {
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
}
};
//8.0之前
QueuedWork.add(awaitCommit);
//8.0之后
QueuedWork.addFinisher(awaitCommit);
//异步执行磁盘写入操作
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
//......其他......
}
复制代码
构造一个名为 awaitCommit 的 Runnable 任务并将其加入到 QueuedWork 中,该任务内部直接调用了
CountDownLatch.await() 方法,即直接在 UI 线程执行等待操作,那么需要看 QueuedWork 中何时执
行这个任务。
QueuedWork 类在 Android8.0以上和8.0以下 的版本实现方式有区别:
8.0之前QueuedWork.java:
8.0之后QueuedWork.java:
public class QueuedWork {
private static final ConcurrentLinkedQueue<Runnable> sPendingWorkFinishers =
new ConcurrentLinkedQueue<Runnable>();
public static void add(Runnable finisher) {
sPendingWorkFinishers.add(finisher);
}
public static void waitToFinish() {
Runnable toFinish;
// 从队列中取出任务:如果任务为空,则跳出循环,UI线程可以继续往下执行;
//反之任务不为空,取出任务并执行,实际执行的CountDownLatch.await(),即UI线程会阻塞
等待
while ((toFinish = sPendingWorkFinishers.poll()) != null) {
toFinish.run();
}
}
//......其他......
}
复制代码
public class QueuedWork {
private static final LinkedList<Runnable> sFinishers = new LinkedList<>();
public static void waitToFinish() {
Handler handler = getHandler();
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
//8.0之后优化,会主动尝试执行写磁盘任务
processPendingWork();
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
try {
while (true) {
Runnable finisher;
synchronized (sLock) {
//从队列中取出任务
finisher = sFinishers.poll();
}
//如果任务为空,则跳出循环,UI线程可以继续往下执行
if (finisher == null) {
break;
}
//任务不为空,执行CountDownLatch.await(),即UI线程会阻塞等待
可以看到不管 8.0 之前还是之后, waitToFinish() 都会尝试从 Runnable 任务队列中取任务,如果有
的话直接取出并执行,直接看哪里调用了 waitToFinish() :
ActivityThread.java
省略了一些代码细节,可以看到在 ActivityThread 中 handleStopActivity 、 handleStopService
方法中都会调用 waitToFinish() 方法,即在 Activity 的 onStop() 中、 Service 的 onStop() 中都
会先同步等待写入任务完成才会继续执行。
所以 apply() 虽然是异步写入磁盘,但是如果此时执行到 Activity/Service 的 onStop() ,依然可能
会阻塞 UI 线程导致 ANR 。
画外音: SP 使用过程中导致的ANR问题,可以通过一些 Hook 手段进行优化,如字节发布的 今日头条
ANR 优化实践系列 - 告别 SharedPreference 等待。我司项目里使用的 SP 也是按此优化,优化后效果还
是比较显著的,所以目前项目也还没有对 SP 进行迁移(如迁移到 MMKV 或 DataStore ),但并不影响我们
学习新的存储姿势。
三、DataStore使用
DataStore优势:
DataStore 基于事务方式处理数据更新。
DataStore 基于 Kotlin Flow 存取数据,默认在 Dispatchers.IO 里异步操作,避免阻塞 UI 线
程,且在读取数据时能对发生的 Exception 进行处理。
不提供 apply()、commit() 存留数据的方法。
支持 SP 一次性自动迁移至 DataStore 中。
3.1 Preferences DataStore
finisher.run();
}
} finally {
sCanDelay = true;
}
}
}
复制代码
private void handleStopActivity(IBinder token, boolean show, int configChanges,
int seq) {
//......其他......
QueuedWork.waitToFinish();
}
private void handleStopService(IBinder token) {
//......其他......
QueuedWork.waitToFinish();
}
复制代码
剩余13页未读,继续阅读
宋祯祯
- 粉丝: 1
- 资源: 7
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 5G网络基础培训课件.zip
- 2024-spring-HIT-CS-大作业
- yolo目标检测项目实验
- downloadFile-1.hc
- C++课程设计:基于Qt的航班信息管理系统
- ADS7822UVerilog驱动,前面传的有点问题
- 基于python的高性能爬虫程序,使用了多线程+缓存+xpath实现的,这里以彼-岸图库为例,实现,仅用于学习交流
- 中分辨率成像光谱仪(MODIS)烧毁面积产品信息MODIS-C6-BA-User-Guide-1.2.pdf
- Screenshot_20240427_172613_com.huawei.browser.jpg
- 关于学习Python的相关资源网站链接及相关介绍.docx
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0