Appium [分享] 解决 hybird 应用中重复获取 WebView,导致页面元素无法识别的问题

扫地僧 · 2016年02月19日 · 最后由 Wang 回复于 2017年12月15日 · 2976 次阅读

问题描述

在测 APP 的业务流,WebView 和 Native 模式耦合在一起。例如:WebView >> Native >> WebView >> 。。。。。。
Appium 貌似有个问题,从 WebView 切换到 NATIVE 后,chromedriver 可能会把第一个 WebView 的缓存驻留在内存中,即便用 driver.close() 方法关闭当前上下文,也无法清除。
这样会导致进入其他的 WebView,读取的还是第一个 WebView 的内容,导致页面元素无法识别。
下面是发现问题到解决问题的一个过程,希望能帮助到遇到过同样问题的同行。

第一版代码:可能大多数人开始都会这么写,这样可能导致无法识别到其他窗口 WebView 的内容。

public void testDemo() {
        try {
            Log.logInfo("开始切换到WebView模式");
            ((AppiumDriver) driver).context("WEBVIEW_com.xx.xx.xx.xx");
            Log.logInfo("成功切换到WebView模式,开始查找WebView元素");
            driver.findElement(By.cssSelector("#myWeidian > div.tpl_part > ul > li.second > span")).click();
            driver.findElement(By.cssSelector("#myWeidian > div.tpl_part > ul > li.last > span")).click();
            driver.findElement(By.cssSelector("#myWeidian > div.tpl_part > div")).click();
            Log.logInfo("开始切换到NATIVE模式");
            ((AppiumDriver) driver).context("NATIVE_APP");
        } catch (Exception e) {
            // anything
        }
    }

第二版代码:我的思路是想通过正常途径解决问题,把当前 WebView 的实例赋给临时对象,用完后调用 close() 方法(quit() 会把整个 Seesion 关闭),还是以失败告终,有点发吼了。

public void testDemo() {
        try {
            Log.logInfo("开始切换到WebView模式");
            AppiumDriver chromeDriver = (AppiumDriver) ((AppiumDriver) driver).context("WEBVIEW_com.xx.xx.xx.xx");
            Log.logInfo("成功切换到WebView模式,开始查找WebView元素");
            chromeDriver .findElement(By.cssSelector("#myWeidian > div.tpl_part > ul > li.second > span")).click();
            chromeDriver .findElement(By.cssSelector("#myWeidian > div.tpl_part > ul > li.last > span")).click();
            chromeDriver .findElement(By.cssSelector("#myWeidian > div.tpl_part > div")).click();
            chromeDriver.close();
            Log.logInfo("开始切换到NATIVE模式");
            ((AppiumDriver) driver).context("NATIVE_APP");
        } catch (Exception e) {
             // anything
        }
    }

第三版代码:软的不行来硬的,放了个绝招,杀 chromedriver 的进程,还真的成功了,有时候真的需要暴力。。。

public void testDemo() {
        try {
            Log.logInfo("开始切换到WebView模式");
            ((AppiumDriver) driver).context("WEBVIEW_com.xx.xx.xx.xx");
            Log.logInfo("成功切换到WebView模式,开始查找WebView元素");
            driver.findElement(By.cssSelector("#myWeidian > div.tpl_part > ul > li.second > span")).click();
            driver.findElement(By.cssSelector("#myWeidian > div.tpl_part > ul > li.last > span")).click();
            driver.findElement(By.cssSelector("#myWeidian > div.tpl_part > div")).click();
            Log.logInfo("开始切换到NATIVE模式");
            ((AppiumDriver) driver).context("NATIVE_APP");
            Tools.killProcess("chromedriver");
        } catch (Exception e) {
            Tools.killProcess("chromedriver");
        }
    }

    public static void killProcess(String processName) {
        try {
            String cmd = isWindows() ? "tskill " + processName : "killall \"" + processName + "\"";
            cmdInvoke(cmd);
        } catch (Exception e) {
            Log.logInfo(e.getMessage());
        }
    }

    public static String cmdInvoke(String cmd) {
        String cmdOut = "";
        BufferedReader br = null;

        try {
            Process p = Runtime.getRuntime().exec(cmd);
            br = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line = null;
            while ((line = br.readLine()) != null) {
                cmdOut = line;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return cmdOut;
    }
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 25 条回复 时间 点赞

顶一下

谢谢分享

请问楼主,我现在想获取某个控件的坐标,或者它的 size,尝试了很多方法都不行,网上也没搜到,比如说我想获取输入法键盘上的搜索按钮的位置,于是我尝试写

driver.find_element_by_name("搜索").get_attribute('location')

这样不行

#3 楼 @gmjdadk
刚回帖我就搜到方法了 = =

扫地僧 [该话题已被删除] 中提及了此贴 07月07日 21:50

频繁的杀 chromedriver 进程可以解决,但同时又引发了这个问题。

2016-07-27 09:12:38:583 - info: --> GET /wd/hub/session/dbb97f5e-2e41-4e26-b664-69ea3919e5ad/contexts {}
2016-07-27 09:12:38:584 - info: [debug] Getting a list of available webviews
2016-07-27 09:12:38:585 - info: [debug] executing cmd: D:\android-sdk\platform-tools\adb.exe -s QMSDU15A24012345 shell "cat /proc/net/unix"
2016-07-27 09:12:38:635 - info: [debug] WEBVIEW_17916 mapped to pid 17916
2016-07-27 09:12:38:636 - info: [debug] Getting process name for webview
2016-07-27 09:12:38:636 - info: [debug] executing cmd: D:\android-sdk\platform-tools\adb.exe -s QMSDU15A24012345 shell "ps"
2016-07-27 09:12:59:775 - error: Unhandled error: TypeError: Cannot read property 'indexOf' of undefined
    at D:\Program Files\Appium\node_modules\appium\lib\devices\android\android-hybrid.js:102:26
    at Function.findIndex (D:\Program Files\Appium\node_modules\appium\node_modules\underscore\underscore.js:620:13)
    at Function._.find._.detect (D:\Program Files\Appium\node_modules\appium\node_modules\underscore\underscore.js:214:15)
    at D:\Program Files\Appium\node_modules\appium\lib\devices\android\android-hybrid.js:100:7
    at [object Object].<anonymous> (D:\Program Files\Appium\node_modules\appium\node_modules\appium-adb\lib\adb.js:180:9)
    at ChildProcess.exithandler (child_process.js:204:7)
    at emitTwo (events.js:87:13)
    at ChildProcess.emit (events.js:172:7)
    at maybeClose (internal/child_process.js:827:16)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5) context: [GET /wd/hub/session/dbb97f5e-2e41-4e26-b664-69ea3919e5ad/contexts {}]

到日志位置出现卡死的情况。

#6 楼 @adfghzhang 可以看下你的代码吗

#7 楼 @quqing

/**
 * 切换到Webview
 */
public void toWeb() {
    List<String> handleList = new ArrayList<String>();
    Set<String> set = driver.getContextHandles();
    Iterator<String> it = set.iterator();
    while (it.hasNext()) {
        handleList.add(it.next());
    }
    driver.debugLog(handleList+"");
            UtilTools.closeChromeDriver();
    driver.context(handleList.get(1));
}

closeChromeDriver() 方法调用你封装的和我封装的方法效果一样。最后卡在 driver.context(handleList.get(1));的位置。

#8 楼 @adfghzhang 在每次退出 webview 以后关闭 chromedriver,下次再进入 webview 会自动打开 chromedriver 进程

#9 楼 @quqing 是会自动打开啊,进程是开启了,但是 appium 就报这个错了,然后卡住

#10 楼 @adfghzhang 看清回复

#11 楼 @quqing 还望明示。我的逻辑是在切换到 WEB 的 context 前杀掉 chromedriver。然后 driver.context("WEB_XXX") 自动重启 chromedriver, 应该跟你的代码逻辑是一回事。

#12 楼 @adfghzhang 只能给出可能的线索:
1.取 webview 的写法,请确认是否真的是第 2 个
2.chromedriver 请在每次切至 native 就 kill,而不是在进入 webview 前临时抱佛脚,不用的时候何必让其存在内存中,从节省资源考虑前者更科学

请问 isWindows() 方法是干嘛的?

#14 楼 @xjjava 判定是 windows 还是 linux 系列,不同操作系统杀进程的方法不同

我没有用 chromeDriver,我直接用的 AndroidDriver,也可以识别网页内容,但是也出现了缓存问题,我该怎么解决?

#16 楼 @xjjava 进程名改一下应该就可以了

修改 PC 机上的进程名?还是修改哪里的进程名?我 PC 上也没装 chrome,我感觉完全是 App 内部的缓存机制导致手机上的浏览器没有重新请求网页地址,导致 appium 没有获取到内容,这种情况应该怎么办呢?

#18 楼 @xjjava 和 chromeDriver 相应的,杀 AndroidDriver 的进程试试

我压根就没有 chromeDriver,只是用一个 AndroidDriver 就可以识别了,但是进入第二页的时候实际上还是显示的第一页的缓存

21楼 已删除

#20 楼 @xjjava 这种情况没遇到过,你发帖在社区里咨询下试试

大神我问个额外问题 0.0,appium 的 dmg 镜像怎么安装 ?我有个 1.5.2 的 dmg,不知道怎么装。。

请问 robotframework 上如果遇到了此问题,怎么定位 driver 杀死进程啊?

@xjjava 你好 我也是遇到切换进入第二个 webview 界面 无法操作 你搞定了吗?

j 回复

同样 webview 页面用 Androiddriver 直接识别,但是跳转到 native 页面或者调起 native 控件时不生效,请问这个问题您解决了吗?我困扰三两天了

gmjdadk 回复

具体啥方法?能分享下吗?感谢

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册