为了账号安全,请及时绑定邮箱和手机立即绑定

源码分析SharePreferences的apply与commit的区别

标签:
Java

apply与commit有什么区别

还是从源码分析来说明问题:


https://img1.sycdn.imooc.com//5d2c827a0001c0b507060574.jpg

image.png

https://img1.sycdn.imooc.com//5d2c827f000129f307270535.jpg

https://img1.sycdn.imooc.com//5d2c82830001613807070408.jpg

image.png

接上图标记B1处,看下:


https://img1.sycdn.imooc.com//5d2c82b400012fcf07160390.jpg


https://img1.sycdn.imooc.com//5d2c82ba00013ed007290427.jpg

image.png

所以这个handler发送消息后,handleMessage会被调用看下:


https://img1.sycdn.imooc.com//5d2c82c00001303707160454.jpg

image.png


https://img1.sycdn.imooc.com//5d2c82ee0001346a07210503.jpg

image.png


上图提到的Runnable对象是哪个呢??就是enqueueDiskWrite方法里的writeToDiskRunnable对象:

https://img1.sycdn.imooc.com//5d2c82f30001db1e07340515.jpg

image.png

接着分析上图提到的标记C1处:


https://img1.sycdn.imooc.com//5d2c82f90001823d07330564.jpg

image.png


上上图提到到写完文件操作之后,会调用postWriteRunnable的run来移除任务:


https://img1.sycdn.imooc.com//5d2c830c000170b507340580.jpg


所以对于apply要有一个重要的认识就是:会创建一个子线程来执行写操作,这个子线程是支持消息循环机制的。

这里还有一个疑问就是在上文开始处标记A提到的,为什么入队进行写操作之前要先   QueuedWork.addFinisher(awaitCommit);,而在写操作完成后又调用 QueuedWork.removeFinisher(awaitCommit),关于这一点你可以这么理解:QueuedWork.addFinisher(awaitCommit)可以理解为标记开始写操作状态,QueuedWork.removeFinisher(awaitCommit)则理解为标记写操作结束状态,为何要如此标记呀?要做标记肯定是要给人看的,到底给谁看呢?这里我举一个例子当Activity生命周期要回调onStop的时候,会调用ActivityThread中的handleStopActivity方法,关于这一点你不明白的话可以参考我的文章
Activity生命周期回调是如何被回调的?

https://img1.sycdn.imooc.com//5d2c83260001705807130788.jpg

image.png


看到上图会调用QueueWork的waitToFinish,而这个方法则是会执行没有完成的写操作,导致主线程被阻塞。所以这里可以得到一个结论apply虽然是另开线程执行写操作,仍然有可能阻塞主线程,上面的例子就是情景之一。如果在Activity Stop的时候,已经写入完毕了,那么万事大吉,不会有任何等待,这个函数会立马返回。但是,如果你使用了太多次的apply,那么意味着写入队列会有很多写入任务,而那里就只有一个线程在写。


如何解决apply导致的ANR问题

下面是头条的方案,主要的原理就是Hook ActivityThread中的Handler 对象,这个handler对象是static,
然后再为这个handler对象设置callback,这样handler会调用callback对象的handleMessage方法,只要我们重写这个callback方法逻辑,就可以跳过原来系统代码,详情参考
头条解决多次调用apply产生ANR的方案

接下来看看commit方法:

https://img1.sycdn.imooc.com//5d2c834e000163e407110461.jpg

image.png

https://img1.sycdn.imooc.com//5d2c83520001169707080336.jpg

image.png

https://img1.sycdn.imooc.com//5d2c83570001468207140574.jpg

image.png

到此两个方法都分析完了,做下总结如下:

  • 如果是在主线程直接使用建议使用apply

  • apply和commit都可以导致ANR,apply虽然在处理写文件操作时会在另一个线程来处理,但是多次调用情况下可能导致ANR,而commit则是直接在当前执行文件写操作。



作者:钟离四郎
链接:https://www.jianshu.com/p/d093cd2486d6


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消