相关文章推荐
从容的饭卡  ·  单细胞 R语言 ...·  6 月前    · 
买醉的沙滩裤  ·  Spring Data JPA and ...·  8 月前    · 
玩命的甜瓜  ·  pandas更新数据库-掘金·  1 年前    · 

安卓测试。等待视图层次结构的根部拥有窗口焦点

32 人关注

在Android Ui测试中,我想点击一个对话框中的旋转器项目,但它弹出了这个错误。

va.lang.RuntimeException: Waited for the root of the view hierarchy to have window focus and not be requesting layout for over 10 seconds. If you specified a non default root matcher, it may be picking a root that never takes focus. Otherwise, something is seriously wrong. Selected Root:
Root{application-window-token=android.view.ViewRootImpl$W@2dac97c7, window-token=android.view.ViewRootImpl$W@2dac97c7, has-window-focus=false, layout-params-type=1, layout-params-string=WM.LayoutParams{(0,0)(fillxfill) sim=#10 ty=1 fl=#81810100 pfl=0x8 wanim=0x1030461 surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x0}, decor-view-string=MultiPhoneDecorView{id=-1, visibility=VISIBLE, width=1600, height=2560, has-focus=true, has-focusable=true, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
. All Roots:
Root{application-window-token=android.view.ViewRootImpl$W@3c913e1, window-token=android.view.ViewRootImpl$W@21b23506, has-window-focus=true, layout-params-type=1002, layout-params-string=WM.LayoutParams{(310,600)(722x480) gr=#10000033 sim=#1 ty=1002 fl=#1860200 fmt=-3 wanim=0x10302db surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x0}, decor-view-string=PopupViewContainer{id=-1, visibility=VISIBLE, width=722, height=480, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
Root{application-window-token=android.view.ViewRootImpl$W@3c913e1, window-token=android.view.ViewRootImpl$W@3c913e1, has-window-focus=false, layout-params-type=2, layout-params-string=WM.LayoutParams{(0,0)(wrapxwrap) gr=#11 sim=#20 ty=2 fl=#1800002 pfl=0x8 fmt=-3 wanim=0x1030462 surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x10}, decor-view-string=DecorView{id=-1, visibility=VISIBLE, width=1136, height=1058, has-focus=true, has-focusable=true, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
Root{application-window-token=android.view.ViewRootImpl$W@2dac97c7, window-token=android.view.ViewRootImpl$W@2dac97c7, has-window-focus=false, layout-params-type=1, layout-params-string=WM.LayoutParams{(0,0)(fillxfill) sim=#10 ty=1 fl=#81810100 pfl=0x8 wanim=0x1030461 surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x0}, decor-view-string=MultiPhoneDecorView{id=-1, visibility=VISIBLE, width=1600, height=2560, has-focus=true, has-focusable=true, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
at android.support.test.espresso.base.RootViewPicker.get(RootViewPicker.java:99)
at android.support.test.espresso.ViewInteractionModule.provideRootView(ViewInteractionModule.java:69)
at android.support.test.espresso.ViewInteractionModule_ProvideRootViewFactory.get(ViewInteractionModule_ProvideRootViewFactory.java:23)
at android.support.test.espresso.ViewInteractionModule_ProvideRootViewFactory.get(ViewInteractionModule_ProvideRootViewFactory.java:9)
at android.support.test.espresso.base.ViewFinderImpl.getView(ViewFinderImpl.java:68)
at android.support.test.espresso.ViewInteraction$1.run(ViewInteraction.java:120)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6117)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)

我已经尝试

onData(allOf(is(instanceOf(String.class)),containsString("A4"))).inRoot(isPlatformPopup()).perform(click());
onView(withText(containsString("A4"))).inRoot(isFocusable()).check(matches(isDisplayed()));
onView(withText(containsString("A4"))).inRoot(withDecorView(not(getActivity().getWindow().getDecorView()))).check(matches(isDisplayed()));

但它们都不起作用... 谁能告诉我如何获得Ralavant根?

android
automated-tests
android-uiautomator
android-espresso
Kimmy
Kimmy
发布于 2016-09-13
8 个回答
Mr-IDE
Mr-IDE
发布于 2022-01-13
已采纳
0 人赞同

当一个系统对话框被显示时--如 "Power Off" "Unfortunately, Launcher has stopped" (一个后台应用程序崩溃了)--而你试图在该对话框可见时运行Espresso单元测试,就会发生这个错误。

图片来源。 安卓4.0模拟器总是有一个崩溃的启动器?

你可以在代码中通过在运行测试前解除系统对话框来解决这个问题。

// Use the 'testing' Context
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
// Alternative: Use the Activity for the Context
MyActivity activityUnderTest = activityTestRule.getActivity();
activityUnderTest.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));

或者使用adb在命令行上发送广播。

adb shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS

导致错误的另一个原因是,当一个后台应用程序被冻结(蚂蚁金服)或运行缓慢,并出现一个系统对话框说"Launcher isn't responding. Do you want to close it? [Wait] [OK]"

图片来源。https://engineering.linkedin.com/blog/2016/08/introducing-and-open-sourcing-test-butler--reliable-android-test

如果你试图在这个对话框可见的情况下运行Espresso测试,测试将全部失败,并显示 "Waitited for the root... "错误。没有简单的方法可以以编程方式关闭这个对话框。Espresso不能点击这些按钮,原因就在这里描述。 在Android Espresso测试中取消警报对话框.然而,一种方法是使用UI Automator在测试开始前,按下对话框中的 "等待 "按钮。

app/build.gradle

dependencies {
    androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0"

ActivityUiTest.kt

companion object {
    @JvmStatic
    @BeforeClass
    fun dismissANRSystemDialog() {
        val device = UiDevice.getInstance(getInstrumentation())
        // If running the device in English Locale
        var waitButton = device.findObject(UiSelector().textContains("wait"))
        if (waitButton.exists()) {
            waitButton.click()
        // If running the device in Japanese Locale
        waitButton = device.findObject(UiSelector().textContains("待機"))
        if (waitButton.exists()) {
            waitButton.click()

ActivityUiTest.java

@BeforeClass
public static void dismissANRSystemDialog() throws UiObjectNotFoundException {
    UiDevice device = UiDevice.getInstance(getInstrumentation());
    // If the device is running in English Locale
    UiObject waitButton = device.findObject(new UiSelector().textContains("wait"));
    if (waitButton.exists()) {
        waitButton.click();
    // If the device is running in Japanese Locale
    waitButton = device.findObject(new UiSelector().textContains("待機"));
    if (waitButton.exists()) {
        waitButton.click();

或者,你可以在命令行上使用adb来发送敲击屏幕 or key strokes来驳回它。比如说。

# On a 320x480 screen, click at screen location [x=233,y=293] to tap an "OK" dialog button.
# Just in case there is a "Launcher isn't responding" system dialog.
adb shell input tap 233 293
# Send keystroke Arrow Right
sleep 3; adb shell input keyevent 22
# Send keystroke Arrow Right again
sleep 3; adb shell input keyevent 22
# Send keystroke Enter to press a button on the dialog
sleep 3; adb shell input keyevent 66

More info:

  • https://groups.google.com/forum/#!topic/android-test-kit-discuss/yIEwus_hjeY
  • How to dismiss system dialog in Android?
  • Android adb shell am broadcast: Bad component name
  • How to use ADB to send touch events to device using sendevent command?
  • 谢谢,这正是我所面临的问题
    StefanTo
    StefanTo
    发布于 2022-01-13
    0 人赞同

    当我在DialogFragment中使用Spinner时,我也遇到了同样的错误。 这是唯一对我有用的代码。

    onView(withText(containsString("A4"))).inRoot(isPlatformPopup()).check(matches(isDisplayed()));
        
    Dielson Sales
    这解决了我在 AlertDialog 内使用Spinner的问题。
    Jamil Rahman
    Jamil Rahman
    发布于 2022-01-13
    0 人赞同

    我遇到了类似的问题,当一个对话框弹出时包含旋转器项目(下拉列表),我的点击不能在任何一个旋转器项目上执行,并且得到了同样的错误。 我找到了一个解决方案,那就是使用 RootMatchers 的 onData() 方法。

    onData(anything()).inRoot(RootMatchers.isPlatformPopup()).atPosition(1).perform(click());
    

    请注意,在的索引值atPosition()是该项目在旋钮列表中的索引值。

    Jing Li
    Jing Li
    发布于 2022-01-13
    0 人赞同

    以防万一,如果它发生在任何人的Travis构建中(有完全相同的日志)。 请检查 this .

    有完全相同的问题,并通过以下方式解决了 用较低的目标版本创建Avd (19) .

    我试过的东西,没有用的。

  • Adding a unlockScreen() @Before method to UI test.

  • Adding / Removing adb shell input keyevent 82 & .

  • 删除不同的 emulator 命令选项 -no-skin -no-audio -no-window 。 现在我有 -no-window ,这很好。

  • 最后,改变

    echo no | android create avd --force -n test -t android-24 --abi armeabi-v7a
    
    echo no | android create avd --force -n test -t android-19 --abi armeabi-v7a
    

    perfectly solve the issue.

    James Jordan Taylor
    James Jordan Taylor
    发布于 2022-01-13
    0 人赞同

    我在CI中的仿真器上运行Espresso测试时周期性地遇到了这个问题。 因为如果有一个测试失败,检查就会失败,所以答案在 https://stackoverflow.com/a/54203607/4520965 对我的用例来说是不够的。 该答案处理了在随后的测试类中驳回ANR对话的问题,但ANR最初出现的测试类仍然会失败。 为了解决这个问题,我在我的基础ui测试类中加入了以下内容,我的所有Espresso测试都继承自该类。

        //Running count of the number of Android Not Responding dialogues to prevent endless dismissal.
        private var AnrCount = 0 
        //`RootViewWithoutFocusException` class is private, need to match the message (instead of using type matching).
        private val rootViewWithoutFocusExceptionMsg = java.lang.String.format(
                Locale.ROOT,
                "Waited for the root of the view hierarchy to have "
                        + "window focus and not request layout for 10 seconds. If you specified a non "
                        + "default root matcher, it may be picking a root that never takes focus. "
                        + "Root:")
        @Before
        fun setUpHandler() {
            Espresso.setFailureHandler { error, viewMatcher ->
                if (error.message!!.contains(rootViewWithoutFocusExceptionMsg) && AnrCount < 3) {
                    AnrCount++
                    handleAnrDialogue()
                } else { // chain all failures down to the default espresso handler
                    DefaultFailureHandler(context).handle(error, viewMatcher)
        private fun handleAnrDialogue() {
            val device = UiDevice.getInstance(getInstrumentation())
            // If running the device in English Locale
            val waitButton = device.findObject(UiSelector().textContains("wait"))
            if (waitButton.exists()) waitButton.click()
        
    denis_lor
    denis_lor
    发布于 2022-01-13
    0 人赞同

    只要更新 travis.yml 中的构建工具即可。

  • In this file .travis.yml : you should have this before_install: - echo yes | android update sdk --all --filter build-tools-26.0.1 --no-ui --force
  • as wel this in .travis.yml :
  • script: echo no | android create avd --force -n test -t android-22
    --abi armeabi-v7a emulator -avd test -no-audio -no-window & 
    android-wait-for-emulator 
    adb shell settings put global window_animation_scale 0 &
    adb shell settings put global transition_animation_scale 0 &
    adb shell settings put global animator_duration_scale 0 &
    adb shell input keyevent 82 &
        
    t0m
    t0m
    发布于 2022-01-13
    0 人赞同

    也许问题是新近更新的,而是新的安卓版本。
    AndroidX测试:1.3.0
    Espresso: 3.3.0
    安卓操作系统。安卓11 (API 30)