SharedPreferences调用导致的ANR分析
来源:互联网 发布:穷人和富人的思维 知乎 编辑:程序博客网 时间:2024/06/05 07:57
转自:http://blog.chinaunix.net/uid-29506893-id-5761774.html
ANR文件提取的有用片段如下:
----- pid 13431 at 2016-09-14 11:46:10 -----
Cmd line: com.android.settings
at java.lang.Object.wait(Native Method)
- waiting on <0x41897ec8> (a java.lang.VMThread) held by tid=1 (main)
at java.lang.Thread.parkFor(Thread.java:1205)
at sun.misc.Unsafe.park(Unsafe.java:325)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:157)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:813)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:973)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1281)
at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:202)
at android.app.SharedPreferencesImpl$EditorImpl$1.run(SharedPreferencesImpl.java:364)
at android.app.QueuedWork.waitToFinish(QueuedWork.java:88)
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:3418)
at android.app.ActivityThread.access$1100(ActivityThread.java:154)
可以看到这里面卡在waitToFinish()上面,该函数的等待导致了ANR的发生,看其函数描述如下:
点击(此处)折叠或打开
- /**
- * Finishes or waits for async operations to complete.
- * (e.g. SharedPreferences$Editor#startCommit writes)
- *
- * Is called from the Activity base class's onPause(), after
- * BroadcastReceiver's onReceive, after Service command handling,
- * etc. (so async work is never lost)
- */
- public static void waitToFinish() {
- Runnable toFinish;
- while ((toFinish = sPendingWorkFinishers.poll()) != null) {
- toFinish.run();
- }
- }
解释为在处理activity、service或广播的时候会被执行,以确保之前加入等待队列sPendingWorkFinishers的异步处理能够执行到结束。果然这里确实被ActivityThread调用,整体函数交互如下:
可以看到写入磁盘等待awaitCommit在apply中被创建,同时加入了QueuedWork(即apply方法会将等待写入到文件系统的任务放在QueuedWork的等待完成队列里)。awaitCommit阻塞在await(判断CountDownLatch为0时返回)。
正常情况下equeueDiskWrite()函数创建线程执行writeToDiskRunnable:
- 当正常写入成功后,在setDiskWriteResult()会将CountDownLatch减值为0;
- 进一步调用postWriteRunnable执行删除QueueWork中的任务。
函数调用如下:
点击(此处)折叠或打开
- private void enqueueDiskWrite(final MemoryCommitResult mcr,
- final Runnable postWriteRunnable) {
- final Runnable writeToDiskRunnable = new Runnable() {
- public void run() {
- synchronized (mWritingToDiskLock) {
- writeToFile(mcr); //写入文件,最终调用mcr.setDiskWriteResult()设置writtenToDiskLatch.countDown();
- }
- synchronized (SharedPreferencesImpl.this) {
- mDiskWritesInFlight--;
- }
- if (postWriteRunnable != null) {
- postWriteRunnable.run(); //这里实际执行的awaitCommit,执行时由于之前countDown操作使得count=0从而await()会直接返回
- }
- }
- };
- ......
- QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
- }
从这里可以梳理出为什么调用QueueWork.waitToFinish()为什么会出现等待:
sPendingWorkFinshers中存在未完成的任务(awaitCommit)<=== postWriteRunnable还没有机会执行(因为执行后在队列中是被删除的)<=== writeToFile()执行未结束(从而也就没有机会去减少CountDownLatch的值)<=== awaitCommit阻塞在CountDownLatch;
看到上面的推论,其中writeToFile最耗时间,在这个异步操作过程中,我们的Activity结束了,然后调用waitToFinish()将会导致hang住。
所以如果我们使用SharedPreference的apply方法, 虽然该方法可以很快返回, 并在其它线程里将键值对写入到文件系统, 但是当Activity的onPause/onStop等方法被调用时,会等待写入到文件系统的任务完成,如果写入比较慢,主线程就会出现ANR问题。
另外,SharedPreference除了提供apply外还提供commit方法,源码如下所示,该方法直接在调用线程中执行,不会转入后台,但如果我们在UI线程commit,且磁盘写入较慢的情况下,ANR依然会发生,这个时候就需要将修改SharedPreference值的任务放到单独线程里去做。
点击(此处)折叠或打开
- public boolean commit() {
- MemoryCommitResult mcr = commitToMemory();
- SharedPreferencesImpl.this.enqueueDiskWrite(
- mcr, null /* sync write on this thread okay */);
- try {
- mcr.writtenToDiskLatch.await();
- } catch (InterruptedException e) {
- return false;
- }
- notifyListeners(mcr);
- return mcr.writeToDiskResult;
- }
所以,使用SharedPreference时无论使用apply还是commit都会有很小的概率由于写入磁盘问题(IO瓶颈)出现ANR。除了IO瓶颈外,锁瓶颈也是SharedPreference的一个缺点,单个共享数据的文件在被同时操作时会有加解锁开销。
总结一下,由于IO瓶颈导致的ANR我们是束手无策的,但我们在使用过程可以优化:
1)对于多个线程访问单个SharedPreference的情况用apply比较好(提高响应速度),只有一个线程访问的话用commit比较好;2)对于修改多个值能一次提交;3)如果真的发现调用经常出现ANR,那就考虑起一个线程在后台来做,这个时候选用commit,而不是apply。
附上一篇文章,其粗略总结了使用SharedPreference的缺点和注意事项:
http://www.cnblogs.com/puff/p/5530825.html
- SharedPreferences调用导致的ANR分析
- 关于调用频繁调用MediaPlayer播放音乐导致ANR的解决办法
- ANR的分析解决
- Android的ANR分析
- Whatsapp ANR的一个分析,MediaProvider的 synchronized 和 beginTransaction 死锁导致的奇葩问题
- Android5.0L因SystemUI ANR导致的黑屏问题分析
- 【警惕】synchronized导致的Android ANR
- android分析monkey的anr
- ANR的一个实例分析
- Android发生ANR 的分析
- Android ANR介绍及分析ANR log信息的方法
- 一次ANR分析:CPU爆炸引起的ANR
- anr 分析
- ANR分析
- ANR分析
- ANR分析
- ANR分析
- ANR分析
- lua 5.1.5安装
- docker --helpmi帮助说明
- 探索Vue之简单双向绑定
- MYSQL函数及导出、导入数据库;执行SQL脚本
- 在浏览器中禁止拖动元素打开新界面
- SharedPreferences调用导致的ANR分析
- Digital Image Processing(M):2.2.1 imadjust和stretchlim
- 第五周(2) 后台代码解耦与客户端具体功能设计
- linux开启对外端口
- Android 布局属性大全(未整理)
- 数组旋转
- Java多线程简单实例
- [android 创建Fragment示例传递参数时的注意事项]
- WEB项目中MVC与MVVM的思考