缘由

之前通过代码在 uiautomator2 server 的源码中添加了弹窗监听器,并通过 Timer 每隔 1 秒执行一次,
导致在部分手机(例如华为荣耀 8,小米 5S)中起 uiautomator2 server 后,获取 get 请求响应超时。

弹窗监听器代码

public void errorHandle() {
        getUiDevice().registerWatcher("installAPP1", new UiWatcher() {
            @Override
            public boolean checkForCondition() {
                /** 安装应用时,检查弹窗并点击允许 **/
                if (getUiDevice().hasObject(By.pkg(Pattern.compile(".*android.*")).text(Pattern.compile(".*允许")))) {
                    Logger.info("installAPP1 window pop up");
                    getUiDevice().findObject(By.pkg(Pattern.compile(".*android.*")).text(Pattern.compile(".*允许"))).click();
                    return true;
                }
                if (getUiDevice().hasObject(By.pkg(Pattern.compile(".*systemmanager")).text(Pattern.compile(".*允许")))) {
                    Logger.info("installAPP1 window pop up");
                    getUiDevice().findObject(By.pkg(Pattern.compile(".*systemmanager")).text(Pattern.compile(".*允许"))).click();
                    return true;
                }
                return false;
            }
        });

        getUiDevice().registerWatcher("installAPP2", new UiWatcher() {
            @Override
            public boolean checkForCondition() {
                if (getUiDevice().hasObject(By.res(Pattern.compile(".*packageinstaller.*")).text(Pattern.compile(".*安装")))) {
                    Logger.info("installAPP2 window pop up");
                    getUiDevice().findObject(By.res(Pattern.compile(".*packageinstaller.*")).text(Pattern.compile(".*安装"))).click();
                    return true;
                }
                /** 安装应用时,点击安装按钮,其Id包含id和button **/
                if (getUiDevice().hasObject(By.res(Pattern.compile(".*id/.*button.*")).text(Pattern.compile(".*安装"))) && !getUiDevice().hasObject(By.res(Pattern.compile(".*packageinstaller.*")).text(Pattern.compile(".*商店")))) {
                    Logger.info("installAPP2 window pop up");
                    getUiDevice().findObject(By.res(Pattern.compile(".*id/.*button.*")).text(Pattern.compile(".*安装"))).click();
                    return true;
                }
                return false;
            }
        });

        getUiDevice().registerWatcher("okButton", new UiWatcher() {
            @Override
            public boolean checkForCondition() {
                /** 适用于HTC手机,安装应用时,点击确定按钮 */
                if (getUiDevice().hasObject(By.text(Pattern.compile(".*应用程序权限"))) && getUiDevice().hasObject(By.text(Pattern.compile(".*确定")))) {
                    Logger.info("okButton window pop up");
                    if(getUiDevice().hasObject(By.res(Pattern.compile(".*id/.*button.*")).text(Pattern.compile(".*确定")))) {
                        getUiDevice().findObject(By.res(Pattern.compile(".*id/.*button.*")).text(Pattern.compile(".*确定"))).click();
                        return true;
                    }else if(getUiDevice().hasObject(By.text(Pattern.compile(".*确定")))){
                        getUiDevice().findObject(By.text(Pattern.compile(".*确定"))).click();
                        return true;
                    }
                }
                /** 点击确认按钮,其Id包含 id和button */
                if (getUiDevice().hasObject(By.res(Pattern.compile(".*id/.*button.*")).text(Pattern.compile(".*确认")))) {
                    Logger.info("okButton window pop up");
                    getUiDevice().findObject(By.res(Pattern.compile(".*id/.*button.*")).text(Pattern.compile(".*确认"))).click();
                    return true;
                }
                /** 点击确定按钮,其Id包含 id和button */
                if (getUiDevice().hasObject(By.pkg(Pattern.compile(".*installer.*")).text(Pattern.compile(".*确定")))) {
                    Logger.info("okButton window pop up");
                    getUiDevice().findObject(By.pkg(Pattern.compile(".*installer.*")).text(Pattern.compile(".*确定"))).click();
                    return true;
                }
                return false;
            }
        });

        getUiDevice().registerWatcher("pressContinue", new UiWatcher() {
            @Override
            public boolean checkForCondition() {
                /** 点击Continue按钮*/
                if (getUiDevice().hasObject(By.clazz("android.widget.Button").pkg("com.android.browser").text("Continue"))) {
                    Logger.info("Continue window pop up");
                    getUiDevice().findObject(By.clazz("android.widget.Button").pkg("com.android.browser").text("Continue")).click();
                    return true;
                }
                return false;
            }
        });

        getUiDevice().registerWatcher("goodButton", new UiWatcher() {
            @Override
            public boolean checkForCondition() {
                /** 点击“好”按钮*/
                if (getUiDevice().hasObject(By.res(Pattern.compile(".*id/.*button.*")).text(Pattern.compile(".*好")))) {
                    Logger.info("goodButton window pop up");
                    getUiDevice().findObject(By.res(Pattern.compile(".*id/.*button.*")).text(Pattern.compile(".*好"))).click();
                    return true;
                }
                return false;
            }
        });

        getUiDevice().registerWatcher("ANR", new UiWatcher() {
            @Override
            public boolean checkForCondition() {
                /** 手机ANR时,点击OK button*/
                if (getUiDevice().hasObject(By.clazz("com.android.server.am.AppNotRespondingDialog"))) {
                    Logger.info("ANR window pop up");
                    if(getUiDevice().hasObject(By.text("OK"))){
                        getUiDevice().findObject(By.text("OK")).click();
                    }
                    return true;
                }
                return false;
            }
        });

        getUiDevice().registerWatcher("ANR2", new UiWatcher() {
            @Override
            public boolean checkForCondition() {
                /** 手机ANR时,点击OK button*/
                if (getUiDevice().hasObject(By.pkg("android").text(Pattern.compile(".*isn't responding.*")))) {
                    Logger.info("ANR2 window pop up");
                    if(getUiDevice().hasObject(By.text("OK"))){
                        getUiDevice().findObject(By.text("OK")).click();
                    }
                    return true;
                }
                return false;
            }
        });

        getUiDevice().registerWatcher("CRASH", new UiWatcher() {
            @Override
            public boolean checkForCondition() {
                /** 手机CRASH时,点击OK button*/
                if (getUiDevice().hasObject(By.clazz("com.android.server.am.AppErrorDialog"))){
                    Logger.info("CRASH window pop up");
                    if(getUiDevice().hasObject(By.text("OK"))){
                        getUiDevice().findObject(By.text("OK")).click();
                    }
                    return true;
                }
                return false;
            }
        });

        getUiDevice().registerWatcher("CRASH2", new UiWatcher() {
            @Override
            public boolean checkForCondition() {
                /** 手机CRASH时,点击OK button*/
                if (getUiDevice().hasObject(By.pkg("android").text(Pattern.compile(".*has stopped.*")))){
                    Logger.info("CRASH2 window pop up");
                    if(getUiDevice().hasObject(By.text("OK"))){
                        getUiDevice().findObject(By.text("OK")).click();
                    }
                    return true;
                }
                return false;
            }
        });

//        new Thread(new Runnable() {
//
//            @Override
//            public void run() {
//                while(true) {
//                    getUiDevice().runWatchers();
//                    getUiDevice().waitForIdle(2000);//避免第一次runWatchers还没有完成,立即运行了第二次
//                }
//            }
//        }).start();

//        TimerTask updateWatchers = new TimerTask() {
//            @Override
//            public void run() {
//                try {
//                    getUiDevice().runWatchers();
//                } catch (final Exception e) {
//                }
//            }
//        };
//        Timer timer = new Timer("WatchTimer");
//        timer.scheduleAtFixedRate(updateWatchers, 100, 2000);

        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                getUiDevice().runWatchers();
                try {
                    getUiDevice().dumpWindowHierarchy(new File("/mnt/sdcard/test.xml"));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }, 0, 2, TimeUnit.SECONDS);

    }

导致的问题

结果是在 get 请求阶段长时间无响应,进而安装应用失败

[UiAutomator2] Starting uiautomator2 server v0.1.0 with cmd: am,instrument,-w,io
.appium.uiautomator2.server.test/android.support.test.runner.AndroidJUnitRunner
[UiAutomator2] running command...
 adb -s 4449d8a9 shell am instrument -w io.appium.uiautomator2.server.test/andro
id.support.test.runner.AndroidJUnitRunner...
[UiAutomator2] Waiting for UiAutomator2 to be online...
[debug] [JSONWP Proxy] Proxying [GET /status] to [GET http://localhost:8200/wd/h
ub/status] with no body
[debug] [JSONWP Proxy] Proxying [GET /status] to [GET http://localhost:8200/wd/h
ub/status] with no body
[debug] [JSONWP Proxy] Proxying [GET /status] to [GET http://localhost:8200/wd/h
ub/status] with no body
[debug] [JSONWP Proxy] Proxying [GET /status] to [GET http://localhost:8200/wd/h
ub/status] with no body
[debug] [JSONWP Proxy] Proxying [GET /status] to [GET http://localhost:8200/wd/h
ub/status] with no body
[debug] [JSONWP Proxy] Proxying [GET /status] to [GET http://localhost:8200/wd/h
ub/status] with no body
……

解决方法

修改监听器的间隔时间,降为 2000,即 2 秒执行一次

TimerTask updateWatchers = new TimerTask() {
            @Override
            public void run() {
                try {
                    getUiDevice().runWatchers();
                } catch (final Exception e) {
                }
            }
        };
        Timer timer = new Timer("WatchTimer");
        timer.scheduleAtFixedRate(updateWatchers, 100, 2000);

原因猜测

可能是因为手机中的相关进程是单例的,频繁的处理弹窗导致来不及响应用户的请求


↙↙↙阅读原文可查看相关链接,并与作者交流