admin管理员组

文章数量:1534197

Monkey跑出的 Launcher ANR,场景为在Launcher的Recents界面下一个Activity启动又快速销毁导致的无焦点窗口问题。

1. log分析

2. 模拟ANR

根据之前的log分析,我们已经可以写一个Demo App来模拟该ANR的发生情况了,总结如下:

1、启动任意一个Activity,我这里写的Demo App为MainActivity。

2、接着输入事件KEYCODE_RECENT_APPS,回到Recents。

3、切回到Launcher后100ms,马上让MainActivity以NEW_TASK的方式调起一个另外一个Activity,我这里用一个名为SingleTaskActivity的Activity去模拟。

4、SingleTaskActivity启动后,马上调用finish,我这里是在onCreate方法中调用的 —— KO,可以复现无焦点窗口的情况,如果此时再输入一个KeyEvent事件,即可发生ANR。

另外把Demo App安装在Pixel上发现没问题:

有可能是Pixel多了一些patch,所以修复了此问题?

3. recents_animation_input_consumer分析

多打些log:

看到及时是发生问题后,其实也是有继续调用InputMonitor.updateInputFocusRequest方法的,但是"recents_animation_input_consumer"却没有获取焦点,继续看看代码是为什么:

这里的RecentsAnimationController是之前的逻辑了,现在点击切换到Recents,RecentsAnimationController也是为空的,那么我们主要看这个逻辑:

                    // Shell transitions doesn't use RecentsAnimationController but we still
                    // have carryover legacy logic that relies on the consumer.
                    || (getWeak(mActiveRecentsActivity) != null && focus.inTransition()
                            // only take focus from the recents activity to avoid intercepting
                            // events before the gesture officially starts.
                            && focus.isActivityTypeHomeOrRecents()

看下"recents_animation_input_consumer"能够获取焦点的log:

逐条看下这三个条件。

3.1 InputMonitor.mActivityRecentsActivity

首先看下这里的成员变量mActivityRecentsActivity的设置逻辑。

当在某一个非Launcher界面点击Recents时会进行设置:

当回到Launcher时则清除:

3.2 focus.inTransition()

点击Recents后只走到onTransitionReady,不会走到finishTransition,所以这个也可以满足。

3.3 focus.isActivityTypeHomeOrRecents()

如果focus时Launcher,这个条件肯定满足,更不用多说。

那么这里说明了,如果“recents_animation_input_consumer”想要取得焦点,只能从Launcher那边拿。如果之前获取焦点的是别的非Home或者Recents类型的窗口,那么“recents_animation_input_consumer”是不能拿到焦点的。

再次回看发生问题时的log,发现阻碍“recents_animation_input_consumer”成为焦点的原因是走到这里时的focus为空:

再看下正常的pixel的情况:

发现是在finishActivity的流程下,将窗口焦点从null切换到了Launcher,接着“recents_animation_input_consumer”就请求了焦点。

再看有问题的情况,发现finishActivity流程下也是去更新了窗口焦点:

但是Launcher的isVisibleRequestedOrAdding返回了false,所以不能取得焦点。

所以问题还是回到了DisplayContent.mCurrent为null,即焦点窗口丢失。

4. 对焦点窗口丢失的原因分析

再看下问题Activity,如之前所说,我这里模拟用的是“SingleTaskActivity”,快速启动又销毁的流程的log信息:

看到这个Activity走finish流程的时候,该流程是有重新去更新焦点窗口的,但是此时由于此时“Window{TclQuickstepLauncher}”的isVisibleRequestedOrAdding仍然为false,所以不满足作为焦点窗口的条件,因此无法将焦点窗口切换为“Window{TclQuickstepLauncher}”。

而当后续“SingleTaskActivity”走pause流程的时候,“TclQuickstepLauncher”的可见性才被设置为true,但是后续没有再调用DisplayContent.updateFocusedWindowLocked去更新焦点窗口了,所以这之后即使“TclQuickstepLauncher”满足了canReceiveKeys的条件,也无法变为焦点窗口了。

那为什么pixel没问题呢,看下pixel的情况,发现从发送keyEvent=312之后,“NexusLauncherActivity”的可见性始终都没有改变过,所以“NexusLauncherActivity”一直都可以满足canReceiveKeys的条件。

而我们的情况则不同,“TclQuickstepLauncher”在它的completePause的流程中设置了可见性为false:

再看“TclQuickstepLauncher”为什么会被pause:

从log看,是“SingleTaskActivity”被启动后,在TaskDisplayArea,pauseBackTasks中被pause:

被pause的原因则是Launcher对应的Task,调用canBeResuemd方法返回了false,所以这里认为该Task中的Activity应该被pause。

TaskFragment.canBeResumed的方法为:

主要的逻辑就是调用TaskFragment.getVisibility去获取当前TaskFragment的可见性。

另外看pixel的情况,反而是“MainActivity”被pause,Launcher没有被pause:

说明pixel的代码和我们的代码在TaskFragment.getVisibility上有差异?拿从集成那边的最新基线比较我们的代码,很快发现了差异点:

打入该patch后问题不再复现。

5. 一点题外话

在问题log中还看到有以下log:

01-26 13:58:02.241 1543 3349 I InputDispatcher: Dropping event because all windows would just receive ACTION_OUTSIDE: MotionEvent

虽然该ANR并不是有MotionEvent触发,但是还是看下这个log打印的场景。

写一个绘制需要15000ms的Activity2,让Activity1启动Activity2的时候finish,此时点击屏幕,即输入MotionEvent,有三个阶段:

1、首先是Activity1还没有被stop的时候,此时屏幕上仍然可以看见Activity1,但是Activity2正在启动,导致了Activity1无法接收事件,因此这里的log是:


02-27 17:52:36.560 1473 1654 I InputDispatcher: Dropping event because all windows would just receive ACTION_OUTSIDE: MotionEvent

2、接着Activity1被stop,Activity2也还没有起来,此时屏幕上是黑屏显示:

此时的log为:
02-27 17:52:39.214 1473 1654 I InputDispatcher: Dropping event because there is no touchable window at (423.4, 734.5) on display 0.
可以看到,虽然任何窗口都无法接收该MotionEvent,但是此时并不会触发ANR的计时,也就是MotionEvent在此场景下不会导致ANR?

3、最后我们再手动输入一个KeyEvent,此时log为:

看到此时才真正触发了ANR的倒计时:
02-27 17:52:48.246 1473 1654 W InputDispatcher: Waiting because no window has focus but ActivityRecord{e0a1ba7 u0 com.example.demoapp/.LongDrawActivity t479} may eventually add a window when it finishes starting up. Will wait for 5000ms

本文标签: 窗口焦点WMSandroidANR