广播超时BroadcastQueue: Timeout of broadcast BroadcastRecord
问题描述
某些业务场景,发出广播10s了,但是Receiver没有及时响应(ANR), 查看日志看到Timeout of broadcast BroadcastRecord
01-01 18:53:21.392 W/BroadcastQueue( 2589): Timeout of broadcast BroadcastRecord{18162c1 u-1 android.intent.action.XXX_XX} - receiver=android.os.BinderProxy@5656678, started 10002ms ago
01-01 18:53:21.392 W/BroadcastQueue( 2589): Receiver during timeout of BroadcastRecord{18162c1 u-1 android.intent.action.XXX_XX} : BroadcastFilter{de5b897 10081/u0 ReceiverList{8e457e6 22351 com.android.aaa/10081/u0 remote:5656678}}
01-01 18:53:21.993 W/BroadcastQueue( 2589): Skipping deliver [foreground] BroadcastRecord{18162c1 u-1 android.intent.action.XXX_XX} to ReceiverList{d139c53 7589 com.android.xxx2//11312/u0 remote:c867686}: process gone or crashing
问题分析
(1) com.android.aaa 的 onReceive 方法中,做了一些耗时操作,或者这个应用占用资源过多, 导致无法及时在onReceive() 方法中结束并返回。
这个时候,onReceive 应该做一些简单的操作,例如启动一个Service 处理,然后返回。
(2) 系统正在忙(CPU),例如Binder 挂掉了
(3) 应用内存泄漏了
问题解决
- onReceive 中操作拆分成异步操作
- 使用service完成任务
SP超时getString、apply、new超时
问题描述
做系统应用,launcherApp首次启动需要加载配置文件,配置文件的数据写在SP中,主线程读取SP中数据过程中,偶现anr
问题分析
1.getString超时分析
查看SP取值的get源码,如从sp 中读取一个 String,会调用到 getString() 方法;
调用 awaitLoadedLocked() 直到该 SP 对象创建完成,所以这里就对导致主线程等待。从上面知道,只有 SP 对应的xml 解析完了,并且创建出 SP 对象,mLoaded 才会是 true,否则就会一直等待。
如果你存储的 SP 特别大,while无限循环,持续等待从磁盘读取xml文件,那么可能就会导致主线程 ANR。
@Nullable
public String getString(String key, @Nullable String defValue) {
synchronized (mLock) {
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
private void awaitLoadedLocked() {
if (!mLoaded) {//sp 对象创建完成,mLoaded 才会是 true
BlockGuard.getThreadPolicy().onReadFromDisk();
}
while (!mLoaded) {
try {
mLock.wait();
} catch (InterruptedException unused) {
}
}
}
2.apply超时分析
apply会阻塞activity的onStop,调用SP的apply,会强制等apply执行完成后,才执行Activity的onStop,造成页面卡顿或ANR
参考阅读下文
commit() 和 apply() 的区别在于:
在调用 QueuedWork.queue() 方法的时候,apply() 是 postDelay() 100毫秒执行的,commit是立刻
public static void queue(Runnable work, boolean shouldDelay) {
Handler handler = getHandler();
synchronized (sLock) {
sWork.add(work);
if (shouldDelay && sCanDelay) {
handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY);
} else {
handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
}
}
}
其次的区别在于apply() 会触发 QueuedWork.addFinisher(awaitCommit),那么这里会导致 waitToFinish,在 QueueWork.java 中;,在 Activity.onStop() ,BroadCastReceiver.onReceive(),Service handleCommend() 的时候,都会去执行这个 waitToFinish(),保证数据不会丢失。
//apply() 方法中
QueuedWork.addFinisher(awaitCommit);
QueueWork.waitToFinish()
例如在 Activity.onStop() 的时候,会调用以下代码:
//ActivityThread.java 中
private void handleStopActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport, int seq) {
.......
// Make sure any pending writes are now committed.
if (!r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
........
}
也就是需要处理完你之前 apply() 提交的内容,该 Activity 才会 onStop(),但是实际上,如果是启动新的 Activity,好像不会有问题,但是如果是回退当前 Activity 的话,可能会因为 SP 的 apply() 操作,卡主当前 Activity 的生命周期。
那么为什么非要 waitToFinish() 呢?因为我们使用 Activity 作为 Context 操作一个 SP,那么实际上如果没有确认该 Activity 不会再次操作 SP,那么新旧 Activity 同时操作 SP 那么这种情况下,非常容易出错,而且会影响效率。
问题解决
- sp数据拆分,分成多个文件,按需读取不同的文件;sp对应的文件尽量不要太大,按照模块名称去读写对应的sp文件,而不是一个整个应用都读写一个sp文件
- 自定义一个 SharedPreferencesImpl,去除 WorkQueue 的 waiteFinish() 的相关逻辑
- 代理 Activity 和 Application 的 getSharedPerfrnences() 方法,返回自定义的 SharedPreferencesImpl
- 尽量不要写入大的 key-value 值,对 key-value 进行强制检查,例如在 putString() 进行长度检查
- 不要同时多次 apply()
- 尽量在子线程读取sp,然后返回到主线程,在操作sp
- 在工作线程中写入sp时,直接调用commit就可以,不必调用apply,这种情况下,commit的开销更小
- 在主线程中写入sp时,不要调用commit,要调用apply
- sp的适合读写轻量的、小的配置信息,不适合保存大数据量的信息,比如长串的json字符串。
- 当有连续的调用PutXxx方法操作时(特别是循环中),当确认不需要立即读取时,最后一次调用commit或apply即可
- Android8.0已经优化过此问题,可以看下是如下优化的
- 清理等待锁-缺点是什么,优点是什么,影响是什么
Input event dispatching timed out sending to
问题描述
一个页面在点击按钮会开启定时任务,执行完定时任务后,会跳转到下一个页面,某些清空下,定时任务执行超时或者异常,不跳转到另外一个页面,用户频繁点击页面按钮开启任务,偶现anr,查看日志发现Input event dispatching timed out sending to
,完整错误堆栈如下:
03-21 21:05:32.174 I/WindowManager( 611): Input event dispatching timed out sending to com.xx/com.xx.ScreenOnDectectFaceActivity.
Reason: Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago.
Wait queue length: 13. Wait queue head age: 10666.3ms.
03-21 21:05:35.689 W/ActivityManager( 611):
Activity stop timeout for ActivityRecord{99a309b u0 com.xx/com.xx.ScreenOnDectectFaceActivity t89}
03-21 21:05:35.697 I/ActivityManager( 611):
Activity reported stop, but no longer stopping: ActivityRecord{99a309b u0 com.xx/com.xx.ScreenOnDectectFaceActivity t89}
问题分析
用户频繁点击按钮,该按钮进行了耗时操作
问题解决
按钮点击后,需要执行的耗时任务操作放在子线程中处理
ANR Warning onLayout time too long问题分析及解决
问题描述
一个ViewPager中频繁刷新,偶发onLayout time too long
问题
02-09 18:08:45.574 15827 15827 D View : [ANR Warning]onLayout time too long, this =com.xx.CardRecyclerView{6332331 VFED..... .F....ID 0,350-1080,1870 #7f090726 app:id/rv_card}time =679 ms
02-09 18:08:45.574 15827 15827 D View : [ANR Warning]onLayout time too long, this =android.widget.RelativeLayout{1651a16 V.E...... ......ID 0,0-1080,1920}time =679 ms
02-09 18:08:45.574 15827 15827 D View : [ANR Warning]onLayout time too long, this =android.support.v4.widget.DrawerLayout{902ff97 VFE...... ......ID 0,0-1080,1920 #7f0901a1 app:id/drawerLayout}time =679 ms
02-09 18:08:45.575 15827 15827 D View : [ANR Warning]onLayout time too long, this =com.xx.NoScrollViewPager{5f10584 VFED..... ......ID 0,0-1080,1920 #7f090cec app:id/vp_main}time =680 ms
02-09 18:08:45.575 15827 15827 D View : [ANR Warning]onLayout time too long, this =android.widget.FrameLayout{f65c16d V.E...... ......ID 0,0-1080,1920}time =680 ms
02-09 18:08:45.575 15827 15827 D View : [ANR Warning]onLayout time too long, this =android.widget.FrameLayout{690c1a2 V.E...... ......ID 0,0-1080,1920 #1020002 android:id/content}time =680 ms
02-09 18:08:45.575 15827 15827 D View : [ANR Warning]onLayout time too long, this =android.widget.LinearLayout{bedfe33 V.E...... ......ID 0,0-1080,1920}time =680 ms
02-09 18:08:45.575 15827 15827 D View : [ANR Warning]onLayout time too long, this =DecorView@82e002d[HomePageAty]time =680 ms
问题原因
频繁点击、频繁请求或其他快速频繁操作触发了onLayout ,同时onLayout 中做了耗时操作,导致anr
问题解决
- 防抖动过滤,只响应第一笔事件
- 防抖动过滤,只响应最后一笔事件
- onLayout不做耗时操作,操作分解成多个异步小任务