一个非耗时操作Input ANR引发的思考

今天喜提测试给的anr , anr报错信息如下 。

Reason: Input dispatching timed out (Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.)
一个非耗时操作Input ANR引发的思考_第1张图片

看到这个 anr 报错信息 ,Input事件导致的anr ,感觉和窗口没有焦点有亿点关系。

ANR 复现

先弄个透明的activity,然后通过FLAG_NOT_FOCUSABLE把window设置成不可获取焦点模式,如下。

classTransparentActivity : Activity() {

        companionobject {
            var activityCallBack: ActivityCallBack? = nullfunopen(activity: Activity, activityCallBack: ActivityCallBack?=null) {
                TransparentActivity.activityCallBack = activityCallBack
                activity.startActivity(Intent(activity, TransparentActivity::class.java))
            }
        }


        overridefunonCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            activityCallBack?.onCreate(this, savedInstanceState)
            val window = getWindow();
            window.setGravity(Gravity.START or Gravity.TOP);
            val params = window.getAttributes();
            params.x = 0;
            params.y = 0;
            params.width = 1;
            params.height = 1;
            window.setAttributes(params);
            getWindow().addFlags(
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
            )
        }
    }

}

   funtestAnr(view: View) {
        TransparentActivity.open(this)
    }
复制代码

运行点击testanr按钮 , 然后按返回键。

果不其然 anr 出现了 , 看anr报错原因确实和线上的报错一模一样。

一个非耗时操作Input ANR引发的思考_第2张图片

ANR 产生原因

通过复现 , 可以判断应该是窗口没有焦点 , 事件无法响应导致的ANR 。

没想到吧主线程没执行耗时操作也会导致ANR的产生 。

一个非耗时操作Input ANR引发的思考_第3张图片

但是这个ANR 你在屏幕上滑动几下就可以规避了 , 如下

可以看到ANR确实没出现了 , 因为我们在屏幕上滑动 , 导致事件可以被启动它的这个Activity响应了。

先不说怎么去监控它的产生 , 先看下它产生的原理 。

Input 产生的ANR原理

说 Input 之前先说下service、broadcast、provide 的anr 原理 。service、broadcast、provide 的 anr 类似于埋炸弹和拆炸弹 , 如果在规定的时间不拆掉埋下的炸弹 , 炸弹就会boom ( 产生Anr )。

input 的anr 和 service、broadcast、provider不同 。 相比service、broadcast、provide 的埋炸弹和拆炸弹机制 ,input更像是拆弹机制。 input的超时机制并非时间到了一定就会爆炸,而是处理后续事件的过程才会去检测是否该爆炸,所以更像是扫雷的过程。input超时机制为什么是扫雷,而非定时爆炸呢?是由于对于input来说即便某次事件执行时间超过timeout时长,只要用户后续在没有再生成输入事件,则不会触发ANR。 这里的扫雷是指当前输入系统中正在处理着某个耗时事件的前提下,后续的每一次input事件都会检测前一个正在处理的事件是否超时(进入扫雷状态),检测当前的时间距离上次输入事件分发时间点是否超过timeout时长。如果没有超过timeout,则会重置ANR的timeout,从而不会爆炸。

但是有个例外,就是当窗口没有焦点的情况 ,如果窗口没有焦点但又有事件给它处理 , 在这种情况下input anr 机制也和service、broadcast、provide 差不多 (埋炸弹拆炸弹机制), 如果后续没有事件在timeout内被响应(去拆炸弹) , timeout后也会产生anr 。

一个非耗时操作Input ANR引发的思考_第4张图片

anr原理大致流程可以看这张图

一个非耗时操作Input ANR引发的思考_第5张图片

ANR的监控

这种ANR , 通过换掉主线程 Looper 的 Printer 方式 , 通过 Choreographer 类的 FrameCallback 方式 ,通过插桩的 方式 , 通过开一个子线程不断去轮询主线程 方式 , 统统肯定都不能监控到的 。

只有监听SIGQUIT 信号才能监控到这种ANR , 具体参考 微信 Matrix for Android

ANR的解决

就本文而言 , 让 activity 的 window 可以获取焦点这个anr就解决了 。

参考文章:

Android修炼系列(32),你理解的 ANR 监控可能一直是错的

Android ANR Waiting because no window has focus问题分析

Input系统—ANR原理分析

你可能感兴趣的:(技术总结,bug,anr,Input,ANR,dispatching)