Appium 求 Appium webview 操作原理深入讲解

yes!! · April 23, 2015 · Last by Lyndon1 replied at December 28, 2016 · 2978 hits
本帖已被设为精华帖!

Appium 操作 native app 是通过手机里的 bootstrap.jar 最终调用 Uiautomator(android4.2 以上)。
那么操作 webview 在手机端是怎么完成的呢?如果是用 webdriver 或 chromedriver,那么他们具体是怎么工作的呢?

共收到 25 条回复 时间 点赞

怎么工作的呢

这个标题。。。换个没有那么具有迷惑性的吧。。。我还以为是介绍原理。。。

操作 webview 在 4.4 或以上 appium 直接用 chromedriver(就是根据你开 appium session 时使用的 caps 转化成 chromedriver 的 caps 然后开启 chromedriver 的 session。因为都遵循 webdriver 规范,简单处理后把请求转发给 chromedriver 就好了)。

chromedriver 源码,有兴趣可以深入探究:https://code.google.com/p/chromium/codesearch#chromium/src/chrome/test/chromedriver/README.txt&q=file:test/chromedriver/README.txt&sq=package:chromium&l=1
chromedriver 的具体原理是通过 chrome 的 remote debugging 来控制 chrome 的。你可以参考这个帖子:
http://testerhome.com/topics/1719
iOS 的 webview:
http://testerhome.com/topics/2047

除此之外,还可以调用 selendroid 控制 webview 。selendroid 控制 webview 使用的是 AndroidAtom.java,简单的说就是往 webview 注入 javascript 事件。
这个帖子的跟帖里各路大神有简单讨论了一下 selendroid/robotium 的 webview 原理:
http://testerhome.com/topics/2234

yes!! #3 · April 23, 2015 Author

#2 楼 @chenhengjie123 十分感谢啊。标题确实有迷惑性,已经改了。。。

刚才正好大致跟了下 appium 中 python 脚本测试 Android app 的过程,初步了解到是:
python 把命令封装成一个 http 请求发给服务端,服务端是一个 nodejs 的 http 服务,nodejs 把收到的请求解析,最终转化成 adb 命令发给手机执行。你说的 webview 一样也是通过 Uiautomator 解析的吧。

#4 楼 @tianya0035 Appium 除了 adb ,还会用 selendroid,uiautomator,chromedriver。

#5 楼 @chenhengjie123 确实是,谢谢。刚在 testin 看了恒温的 appium 介绍,推荐楼主也去看下哈

既然测试 webview 是通过 webdriver 协议,而且是将协议转发给 chromedriver 来操作,那么我就想不用 appium,直接用 webdriver 来测试 webview,但是现在运行的时候有一个问题:unknown error: Failed to run adb command, is the adb server running? 创建 session 的时候,就会报 adb 错,但是我已经 adb start-server 了。求指点

#7 楼 @fengyan77 运行下 adb start-server

@chenhengjie123
我的 QQ 号是 348940981,最近在研究如何在手机上识别 WebView 元素,我用最新的 Appium Inspector 查看天猫 Apk,点击 Refresh,通过日志发现它把 apk 做了 debug 重签名,然后调用了 Bootstrap 里面的 source 命令,在 Inspector 的界面上就可以看到当前 App 页面的控件树。
想请教大家,Refresh 操作是通过 source 实现的吗?能够直接获取到 WebView 的内容?
https://github.com/appium/appium/blob/master/lib/devices/android/android-hybrid.js又在什么时候起的作用呢?
最重要的问题是不依赖 PC 端的 chromedriver,能直接在手机端获取 WebView 元素么?
还请大家不吝赐教,谢谢了!

#9 楼 @our166

  1. Refresh 操作应该和用 appium client api 的 get_source() 请求效果一致,都是向 appium server 发送获取控件树的请求。具体你可以用 appium client api 调用一下看请求是否一致
  2. 这个应该是不能获取到 WebView 内容的。在默认模式下(非 selendroid),对于 native 部分 appium 是通过 uiautomator 获取和控制 app ,webview 部分则是用 chromedriver 。
  3. 你提到的 android-hybrid.js 是处理切换 context 和把请求转给 chromedriver 相关事务的。你可以从函数名看出大致的用处。
  4. 不依赖 PC 端的 chromedriver 直接从手机端获取 Webview 元素目前用 appium 默认模式应该做不到。不知道你具体想实现什么?

@chenhengjie123
非常感谢您的答复,
1.从日志看,Refresh 操作的确是调用了 Bootstrap 里面的 source 方法。
2.从 Inspector 的界面上看,的确是获取到了 webview 元素,以天猫 apk 为例,首页的 banner 点击进去是 webview 的,使用 uiautomatorviewer 只能识别是 webview,内容看不到,但是 Inspector 就识别出来了,不解的是,在 Appium 打开的情况下,uiautomatorviewer 也可以获取到 webview 里面的内容。
3.不太明白 chromedriver 在整个过程中的作用,因为我把 build 目录下的 chromedriver 移走,Inspector 还是可用(webview 元素可以识别)
4.我们想要实现的是在手机中用类似 uiautomator 的方法获取 webview 的元素,这样 app 的遍历测试工具可以对 webview 做进一步的遍历测试。

#11 楼 @our166 在 uiautomatorviewer 里也可以识别 webview 元素这个以前有留意过,貌似用过一次 chromedriver 后就能获取到里面的元素了。当时猜测获取元素时顺带做了一层映射,但后面没有深究。

如果你没有 switch context 到 webview ,应该不会用到 chromedriver 。

@chenhengjie123 今天做的验证:
1.将 chromedriver 移除,通过 Inspector 仍然可以识别 webview 里面的页面元素
2.将 Appium Server 保持启动,使用 uiautomatorviewer 同样可以识别该 App 的页面元素,换 App 无效,停止 Appium Server 无效
3.并未进入 android-hybrid.js 和 selendroid.js(未打印调试日志)
请教 Appium 是通过什么方式获取到 webview 里面的页面元素呢?应该关注哪部分代码?非常感谢!

#13 楼 @our166 我想先确认下,inspector 识别到的 webview 中元素的类是类似 Android.view.view 这样的吗?

@chenhengjie123 @seveniruby 我使用的 Appium 版本是 1.4.16.1,测试的天猫 10002119@tmallandroid_5.16.0.apk
打开天猫顶栏的 banner,里面是一个 android.webkit.WebView,Inspector 可以识别到里面的 android.view.View 以及 android.widget.ListView。有个现象是 Appium 对待测 APK 做了 debug 签名,导致 apk 在内容展示上跟安装原始版本有一定差异。
我打算跟一下 switch_context 和 pageSource,看看 Inspector 的刷新操作到底是一个什么流程。看能否把这个动作移植到手机上?
做这个分析的背景是,我们做了一个 app 的遍历工具,基于 uiautomator 在手机上运行,所以希望同样能在手机识别 webview 元素。
请两位赐教!多谢!

@chenhengjie123
我复现了http://www.cnblogs.com/tobecrazy/p/4905917.html 里面提到的 android.webkit.WebView 可以当成 Nativ_App 处理。单纯调用 Bootstrap 里面的 source 方法没法获取到 android.webkit.WebView 里面的元素,但是 getPageSource 方法就可以。getPageSource 好像是 selenium 里面的方法,你知道它除了调用 Bootstrap 里面的 source 方法外还做了什么吗?谢谢!

#16 楼 @our166 你看下在调用 getPageSource 函数时 appium server 收到的请求路径是什么(会有条类似 --> GET /xxx 这样的日志)。

我看了下源码,获取 source 最终是通过遍历元素树获取的。遍历的关键代码:https://github.com/appium/appium/blob/v1.4.16/lib/devices/android/bootstrap/src/io/appium/uiautomator/core/AccessibilityNodeInfoDumper.java

这部分代码是来自于 android 系统里面 uiautomator 的相关代码,只是 appium 修复了其中的一些 bug 。具体你可以对比下两者的代码。

我觉得有时候能获取 Webview 里面的内容有时候不行,这个应该是有规律的。你可以看下这个规律是什么,能帮助定位问题原因。这也算是一个未解之谜了。

PS:appium 可以设定不重签应用的。使用默认模式时重不重签都可以的。

加精原因:讨论区。

#16 楼 @our166 在你的试验基础上再做了几个试验:

  1. 启动手机后打开包含 webview 的页面,使用 uiautomatorviewer 查看 webview 内部元素是否能查看。结果: 无法查看 webview 内部元素
  2. 启动手机后使用 appium 开启一次 session 然后马上关闭 appium server ,再次使用 uiautomatorviewer 查看 webview 内部元素。结果: 可以查看 webview 内部元素
  3. 在 2 个场景后重启手机,再次使用 uiautomatorviewer 查看 webview 内部元素。结果: 无法查看 webview 内部元素

从这三个场景可以推出:appium server 启动 bootstrap 后 android 系统的 uiautomator 程序发生了变化,同一个函数在启动前后表现不一样了。

其中很有可能就是上面提到的那个 AccessibilityNodeInfoDumper.java 做的。你可以从这个方向研究一下。

#18 楼 @lihuazhang 这个我觉得可以探究完后整个原因后新开个帖子再加精吧。这样加精大多数人还是看不到。

#19 楼 @chenhengjie123 对比了两个文件的源代码,没发现有做了什么特殊的处理。
我也尝试了下面的方式

  1. 启动手机后打开包含 webview 的页面,使用 uiautomatorviewer 查看 webview 内部元素是否能查看。结果: 无法查看 webview 内部元素
  2. 通过 chrome 浏览器 inspect点击对应 webview 的 inspect 按钮后,使用 uiautomatorviewer 查看 webview 内部元素是否能查看 结果: 可以查看 webview 内部元素

目前还不太明白 inspect 到底做了什么就可以查看到了。

#21 楼 @zsx10110 难道是激活了 webview 的某些隐藏功能?

这确实是个千古之谜了。估计得看 webview 的源码才行。

收藏讨论区

各位朋友,这个问题你们遇见过吗?启动的时候报错

我今天解决了切换问题,当切换到 WebView 时如果出错我就循环几遍,有的界面一次就切换成功,而有的大概 5、6 次系统则会切换成功,我猜想是缓存问题,切换过程中其实后在更新,不知道理解的对不?

子非鱼 行业流行测试框架对比 中提及了此贴 18 Mar 21:59
需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up