广播超时、input event超时等问题、SP超时getString#apply#new超时、Input event dispatching timed out sending to\ANR Warning onLayout time too long问题分析及解决

广播超时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不做耗时操作,操作分解成多个异步小任务
  •