广播超时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) 应用内存泄漏了

问题解决

  1. onReceive 中操作拆分成异步操作
  2. 使用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 那么这种情况下,非常容易出错,而且会影响效率。

问题解决

  1. sp数据拆分,分成多个文件,按需读取不同的文件;sp对应的文件尽量不要太大,按照模块名称去读写对应的sp文件,而不是一个整个应用都读写一个sp文件
  2. 自定义一个 SharedPreferencesImpl,去除 WorkQueue 的 waiteFinish() 的相关逻辑
  3. 代理 Activity 和 Application 的 getSharedPerfrnences() 方法,返回自定义的 SharedPreferencesImpl
  4. 尽量不要写入大的 key-value 值,对 key-value 进行强制检查,例如在 putString() 进行长度检查
  5. 不要同时多次 apply()
  6. 尽量在子线程读取sp,然后返回到主线程,在操作sp
  7. 在工作线程中写入sp时,直接调用commit就可以,不必调用apply,这种情况下,commit的开销更小
  8. 在主线程中写入sp时,不要调用commit,要调用apply
  9. sp的适合读写轻量的、小的配置信息,不适合保存大数据量的信息,比如长串的json字符串。
  10. 当有连续的调用PutXxx方法操作时(特别是循环中),当确认不需要立即读取时,最后一次调用commit或apply即可
  11. Android8.0已经优化过此问题,可以看下是如下优化的
  12. 清理等待锁-缺点是什么,优点是什么,影响是什么

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

问题解决

  1. 防抖动过滤,只响应第一笔事件
  2. 防抖动过滤,只响应最后一笔事件
  3. onLayout不做耗时操作,操作分解成多个异步小任务
posted on 2024-06-01 11:58  杨超凡随笔的地方  阅读(72)  评论(0编辑  收藏  举报