Appium CrossWalk Hybrid 混合应用 webview 无法获取到 (Android)

来了不是深圳人 · 2016年06月21日 · 最后由 wang 回复于 2017年10月23日 · 2831 次阅读

公司的 App 是 Hybrid 混合应用,95% 页面由 html5 实现,webview 没有使用原生的 webview,是使用的名字叫 crosswalk 的 webview。我现在的问题是在 html5 页面打印 webview 打印出来只有 NATIVE_APP,无法切换到 webview。

开发在代码里面有加 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WebView.setWebContentsDebuggingEnabled(true);}
可以在谷歌浏览器里面输入 chrome://inspect/#devices 进行调试,但是在在 Appium 打印出来就只有 NATIVE_APP
联系了 Android 开发,他也不懂 Appium,根据我给他的信息他搜索了一下,找到一篇帖子(链接如下):
https://github.com/appium/appium/issues/4597#issuecomment-164540456
里面说要改 android-hybrid.js 这个文件。我找到这个文件之后做了如下修改 (标识部分为修改区域):

修改之后打印出来 webview 还是 NATIVE_APP,请教一下是什么原因?

调试代码如下:


# -*- coding:utf-8 -*- 
import os
import unittest
from appium import webdriver
from time import sleep

desird_caps={}
desird_caps['platformName'] = 'Android'
desird_caps['deviceName'] = '76P4C15909025320'
desird_caps['udid'] = '76P4C15909025320'
desird_caps['appPackage'] = 'com.my.shop'
desird_caps['appActivity'] = 'com.my.shop.activity.SplashActivity'
desird_caps['platformVersion'] = '5.1.1'
desird_caps['automationName'] = 'crosswalk'
driver = webdriver.Remote('http://localhost:4723/wd/hub',desird_caps)

def webview():
    contexts=driver.contexts
    for cotext in contexts:
        print cotext

def login():
    driver.find_element_by_id('com.my.shop:id/tv_login').click()
    driver.find_element_by_id('com.my.shop:id/et_phone_number').send_keys(180****5829)
    driver.find_element_by_id('com.my.shop:id/et_password').send_keys(123456)
    driver.find_element_by_id('com.my.shop:id/tv_login').click()
    sleep(5)

def entershop():
    driver.find_elements_by_class_name('android.view.View')[6].click()

sleep(5)
login()
entershop()
webview()

共收到 27 条回复 时间 点赞

1.5.3 亲测!

前提

你已经安装好了 appium,无论是 GUI 还是 npm 的。

打补丁

从这里下载 https://github.com/appium/appium/files/320185/webview_helpers_cordova_suppport.patch.zip,解压。
然后进入到 appium/node_modules/appium-android-driver/ 这个目录下,进行补丁:

patch -p1 lib/webview-helpers.js /path/to/patch/file/webview_helpers_cordova_suppport.patch

打好补丁后,重新把 appium-android-driver build 一下:

npm install

下载打过补丁的 chromedriver

https://github.com/piotrekkmt/chromedriver-appium/blob/master/chromedriver 下载打过补丁的 chromedriver。
将这个目录下 appium/node_modules/appium-android-driver/node_modules/appium-chromedriver/chromedriver/mac/下的 chromedriver 替换成打过补丁的。

启动 Appium 即可

客户端的关键代码如下:


import os
import unittest
from appium import webdriver
from time import sleep

pkg_name = 'com.xxx.bbb'
desird_caps={}
desird_caps['platformName'] = 'Android'
desird_caps['deviceName'] = 'Samsung'
desird_caps['udid'] = '2bcf97ef'
desird_caps['appPackage'] = pkg_name
desird_caps['appActivity'] = com.xxx.bbb.activity.SplashActivity'
desird_caps['platformVersion'] = '5.1.1'


ANDROID_DEVICE_SOCKET = pkg_name + "_devtools_remote"
desird_caps['androidDeviceSocket'] = ANDROID_DEVICE_SOCKET
chromeOptions = {}
chromeOptions['androidDeviceSocket'] = ANDROID_DEVICE_SOCKET
desird_caps['chromeOptions'] = chromeOptions;

driver = webdriver.Remote('http://localhost:4723/wd/hub',desird_caps)

运行过程中会返回一个没有名字的 webview:

[debug] [ADB] Getting connected devices...
[debug] [ADB] 1 device(s) connected
[debug] [ADB] Running /Applications/android-sdk/platform-tools/adb with args: ["-P",5037,"-s","2bcf97ef","shell","cat","/proc/net/unix"]
[debug] [AndroidDriver] Available contexts: ["NATIVE_APP","WEBVIEW_"]
[MJSONWP] Responding to client with driver.getContexts() result: ["NATIVE_APP","WEBVIEW_"]
[HTTP] <-- GET /wd/hub/session/077ca253-cd31-41da-8ce1-7c2bf0cbb107/contexts 200 80 ms - 97
[BaseDriver] Shutting down because we waited 60 seconds for a command

#4 楼 @Lihuazhang

ANDROID_DEVICE_SOCKET = pkg_name + "_devtools_remote"
desird_caps['androidDeviceSocket'] = ANDROID_DEVICE_SOCKET
chromeOptions = {}
chromeOptions['androidDeviceSocket'] = ANDROID_DEVICE_SOCKET

请问这一部分用 java 怎么写?原谅我是 Python 菜鸟

恒温 回复

???

wang 回复

不清楚,好久没看这个了。

楼主最后 Windows 上的问题解决吗

#4 楼 @lihuazhang

该解决方案在 mac 上使用 Appium1.5.3 验证成功,成功打印出来了 WEBVIEW: NATIVE_APP,CHROMIUM ,切换到 CHROMIUM 成功后用 xpath 成功定位元素,不同的手机上打印出的名字可能不一样。

非常感谢@lihuazhang 。小菜鸟遇到疑难杂症的心情是崩溃的,第一次遇到这个问题是两个月前了。


遇到 Hybrid 应用怎么做:

1.先确定开发在测试版本的包里面是否有加调试代码 (一般都有加):if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WebView.setWebContentsDebuggingEnabled(true);}

如何确定有加,连接好电脑打开应用并停留在 H5 页面,在 Chrome 浏览器中输入 chrome://inspect,按回车会显示设备名字,并且会显示安装包名。点 inspect 就会在 Chrome 浏览器显示 App 的页面,就可以在浏览器上定位元素了。
注意:这一步 Chrome 浏览器要 ***,不然打不开

2.写几条简单的操作步骤进入到 html5 页面,打印 webview: print driver.contexts,如果除了 NATIVE_APP 还有一个的话那说明能切换 webview。切换 webview:driver.switch_to.context('打印出来 webview 名字'),

这样,切换 webview 之后,对第一步中找到元素的 xpth,css 进行定位操作。。

居然已经有解决方案了。

还是通过 class 写一个 list 来定位吧,或者要原生的 h5 页面,h5 页面查到元素之后再放到 app 中操作,嵌套 h5 的话元素一般是不会变的。

crosswalk 和原生 webview 使用的 socket 不一样。appium 根据原生 webview 的规则去找会找不到是正常的。

我觉得你理解错了那位作者说的意思。他给的解决方案是直接用他自己的 docker 里面的 appium(已经添加了对 crosswalk 的支持):https://github.com/hamsterksu/appium-xwalk

具体修改是这样的:

  1. https://github.com/hamsterksu/appium-xwalk/blob/master/files/appium-pathces/android-hybrid.js.patch 里的变更应用到 appium 的 appium/lib/devices/android/android.js 文件中
  2. https://github.com/hamsterksu/appium-xwalk/blob/master/files/appium-pathces/android.js.patch 里的变更应用到 appium 的 appium/lib/devices/android/android-hybrid.js
  3. https://github.com/hamsterksu/appium-xwalk/blob/master/files/xwalkdriver64_xwalk_15 这个文件放到 docker 容器的 /opt/xwalkdriver/ 目录中
  4. 启动 appium 时添加 --chromedriver-executable=/opt/xwalkdriver/xwalkdriver64_xwalk_15

简单来说,在 server 端添加了能识别 crosswalk 的 webdriver 使用的 xwalkOptions 的相关代码,并把 chromedriver 执行文件改为 crosswalk 的。

但我觉得其实不全,缺少了识别 crosswalk 进程的相关变更(获取 context 时需要用到)。也许这部分内容他在源镜像里已经修改了。

PS:不知道你的 appium-crosswalkdriver 这个库是哪里找到的?我在你给的链接里没找到。。。

#4 楼 @lihuazhang 赞~

另外发个帖吧,现在这方面的需求应该不少。

#5 楼 @chenhengjie123 让楼主在 check 一遍吧

之前也一直是用 hamsterksu 的 docker 来解决 crosswalk 的问题,不过替换的方案只适用 1.5 以下的 appium,重构后的 appium 不知道怎么改。windows 下也只能用 docker 来跑,楼主的这个只能适合 mac 环境么 还是其他的环境也适合?

#7 楼 @wang04170 只有 mac 有 1.5.3 版本的 Appium,Android 最高只到 1.4.16,mac 上的 android 环境没装,正在研究@chenhengjie 给的 windows 解决方案,你解决过这样的问题?能留个 Q 么

#3 楼 @chenhengjie123 不清楚 appium 的结构,原理。改的也就不对

#3 楼 @chenhengjie123 hamsterksu 的 docker 我有在 windows 和 linux 上都试过是可以的,只不过它是把 webview 的识别替换成了 xwalk 的了,context 的获取还是用 webview 的 api 来获取,然后获取到的 webview 的 context 的格式也还是'WEBVIEW_XXXX',swich 过去后是实际上是在操作 xwalk 的,只不过它的 dokcer 是 1.4.16 的版本。 想问下@lihuazhang的那个打过补丁的 chromedriver 只能在 mac 版本使用么 。。。

我原来用的 xwalkdriver,只提供了 linux 的,自己编了个 windows 版本,替代 chromedriver,然后按照上面说的改下 appium 的代码就可以用了

—— 来自 TesterHome 官方 安卓客户端

#3 楼 @chenhengjie123 webview 和 crosswalk 的识别方式不一样,新的 appium 好像可通过启动参数设置,我当时是直接修改代码的

—— 来自 TesterHome 官方 安卓客户端

#12 楼 @yyswly windows 上解决方案求分享

我也遇到了这样的问题,不过,我用了 uiautomatorviewer 直接看元素的 id classname 等 ,没有去取 webview.!

自己弄个 android demo ,让一个 webview 显示的是手机百度页面,

import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Set;

import org.junit.Test;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;

@SuppressWarnings({ "rawtypes", "unused" })
public class Appium {

    @Test
    public DesiredCapabilities chacha() {
        final DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability(CapabilityType.BROWSER_NAME, "");
        capabilities.setCapability("deviceName", "emulator-5554");
        capabilities.setCapability("platformVersion", "5.1.1");
        capabilities.setCapability("platformName", "Android");
        capabilities.setCapability("appPackage", "vipdd.club.chacha");
        capabilities.setCapability("appActivity", "vipdd.club.chacha.MainActivity");

        return capabilities;
    }

    public static void main(String[] args) throws InterruptedException {
        Appium appium = new Appium();

        // appium.chacha();
        // appium.iulicai();

        try {
            AppiumDriver<?> driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), appium.chacha()

            );

            Set<String> contextNames = driver.getContextHandles();
            for (String contextName : contextNames) {
                System.out.println(contextName);
            }
            //Thread.sleep(20000);
                        //点击OK 显示出手机百度页面
            driver.findElementById("vipdd.club.chacha:id/btnOK").click();
            driver.findElementsByClassName("android.widget.EditText").get(1).sendKeys("appium");
            driver.findElementsByClassName("android.widget.Button").get(1).click();
            // 根据 content-desc 查找元素
            //driver.findElementByAccessibilityId("我的账户").click();
            //driver.findElementByAccessibilityId("请输入手机号码").sendKeys("abc");

        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
}



也不知道是不是这样

#16 楼 @lijiuyi 简单的页面通过遍历 class 是可以定位的,复杂页面的定位就不行了

#2 楼 @leizi0715 我切换到 webview 后,定位到 html5 元素放到 app 中操作,为什么他不执行呢,也不报错。就是没有反应,android 版本 4.4.4.

#18 楼 @nsm 要切换到 webview 模式去运行吧 应该是这样的 webview 我页不怎么用的

LZ 现在问题解决了么,我也遇到这个问题。

#4 楼 @Lihuazhang 我按照你的方法是可以看到 WEBVIEW,但是切换到 webview 进行 findElement 的时候却一直报 SessionNotFoundException:no such session 的错误。

恒温 回复

我按这个步骤已经配置完成,Chromedriver 也下了 crosswlak 补丁的这一版本。上下文也打印出来,但是当我切换 webview 的时候报错,是因为这个 chromedriver 的补丁版本不对吗 ??
提示

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