ATX Android WebView 研究笔记

codeskyblue · August 29, 2018 · Last by z1073336883 replied at October 12, 2018 · 11489 hits
本帖已被设为精华帖!

Android WebView 研究笔记

记录一下省的忘记了,遍看源码,边猜测,边实验

chromedriver用了哪些命令和资源

目前Android WebView支持主要还是靠ChromeDriver来实现的。

Chromedriver貌似一定要ADB才行。其中用到了命令有 ps, grep, adb forward, am start 等等

来源: https://github.com/bayandin/chromedriver/blob/907b958e09dbfdafb13e9257b181009d0ab43b4a/chrome/adb_impl.cc

既然还用到了adb forward .. localabstract, 先打开一个带webview的app(这里用了微信)
查看下有哪些服务监听了localabstract

$ cat /proc/net/unix | grep @
0000000000000000: 00000002 00000000 00010000 0001 01 32803 @bthcitraffic
0000000000000000: 00000002 00000000 00010000 0001 01 3789231 @chrome_devtools_remote
0000000000000000: 00000002 00000000 00010000 0001 01 32963 @/data/misc/bluedroid/.a2dp_ctrl
0000000000000000: 00000002 00000000 00010000 0001 01 76 @time_genoff
0000000000000000: 00000002 00000000 00010000 0001 01 88 @android:debuggerd
0000000000000000: 00000002 00000000 00010000 0001 01 4640516 @webview_devtools_remote_13680
0000000000000000: 00000002 00000000 00010000 0001 01 17596 @jdwp-control
0000000000000000: 00000003 00000000 00000000 0001 03 4453235 @jdwp-control
0000000000000000: 00000003 00000000 00000000 0001 03 4250761 @jdwp-control

其中@符号代码监听的本地Unix socket
这里看到有两个 @chrome_devtools_remote@webview_devtools_remote_13680 比较吸引人。
chrome_devtools_remote应该对应的是浏览器。
webview_devtools_remote_13680对应的应该是应用内的WebView,根据源码感觉这个13680应该是个pid

odin:/ $ ps | grep 13680
USER PID PPID VSIZE RSS WCHAN PC NAME
u0_a280 13680 591 2522104 168072 SyS_epoll_ 0000000000 S com.tencent.mm:tools

com.tencent.mm这个应该就是微信了。

先把这个forward到本地

$ adb forward tcp:5000 localabstract:webview_devtools_remote_13680

看chromedriver的代码,里面自带一些http接口,具体看下面的代码
https://github.com/bayandin/chromedriver/blob/33218feb63bc972c7175390ee2302fe5a2f25056/chrome/devtools_http_client.cc#L92

有个比较简单的接口获取浏览器的版本号,以此来确定该使用的chromedriver版本。

$ curl localhost:5000/json/version
{
"Android-Package": "com.tencent.mm",
"Browser": "Chrome/57.0.2987.132",
"Protocol-Version": "1.2",
"User-Agent": "Mozilla/5.0 (Linux; Android 7.1.1; OD103 Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/044205 Mobile Safari/537.36",
"V8-Version": "5.7.492.72",
"WebKit-Version": "537.36 (@user1128ad40ab2e3d761c06d1)"
}

从这个链接里面可以看到没有Chrome Version(57)对应的应该使用的chromedriver版本号(2.29) https://chromedriver.storage.googleapis.com/2.41/notes.txt

去这个地址下载 https://npm.taobao.org/mirrors/chromedriver

继续更新

猜测:除了微信有个调试的按钮,其他的H5的应用应该也是可以的吧。
先试了试Appium自带的demo apk
https://github.com/appium/java-client/blob/master/src/test/java/io/appium/java_client/ApiDemos-debug.apk
安装到手机上之后,进入Views -> WebView,然后看到这么一个简单的界面

图层结构显示也有Hierarchy

Chrome浏览器打开 chrome://inspect

更多相关的内容参考谷歌的Chrome-Devtools文档 https://developers.google.com/web/tools/chrome-devtools/remote-debugging/?hl=zh-cn

在浏览器里面并没有发现当前这个应用的 webview。
重新读了下谷歌文档-远程调试 WebView

需要在应用中开启WebViewDebugable模式才行,也就是说默认不开启喽。

使用VXP强制开始WebView

那有没有强制开启的方法呢? 很快谷歌出来一篇文章
强制开启android webview debug模式使用Chrome inspect

看来手机需要Root才行,还用到了高端的Xposed(不对呀,我明明记得可以不用root的呀),继续谷歌了下发现还有个VirtualXposed可以不用root
相关的VirtualXposed(简称VXP)简介 https://github.com/android-hacker/VirtualXposed
安装很简单,跟普通的App一样,下载apk到手机,安装就ok了。
接下来用VXP自带的安装器,安装 ApiDemos-debug.apk
然后再安装个插件 WebViewDebugHook 同样是个Apk下载(官方地址:https://github.com/feix760/WebViewDebugHook),用VXP安装上就好了。

关于那个插件的原理可以在这里看到 https://www.jianshu.com/p/d6699cd4505e

安装上之后,先打开

进入到模块中,勾选

返回,然后向下滑动,进入到这个界面,点击箭头位置

拖到最下面点击 重启 是插件更改生效。

PS: 使用VXP安装的应用pm list packages是看不到的,只有在运行的时候用ps才可以查看到

回到应用界面,点击刚才安装的APK API Demos, 重新进入到WebView,然后打开chrome://inspect
这个时候终于看到了,期望的界面。

应用使用的Chrome版本是 62.0.3202.84
Appium的代码里面有个对应的表可以直接查改用哪一个 chromedriver https://github.com/appium/appium-chromedriver/blob/master/lib/chromedriver.js#L20

PS D:\Temp> .\chromedriver.exe -h
Usage: D:\Temp\chromedriver.exe [OPTIONS]

Options
--port=PORT port to listen on
--adb-port=PORT adb server port
--log-path=FILE write server log to file instead of stderr, increases log level to INFO
--log-level=LEVEL set log level: ALL, DEBUG, INFO, WARNING, SEVERE, OFF
--verbose log verbosely (equivalent to --log-level=ALL)
--silent log nothing (equivalent to --log-level=OFF)
--version print the version number and exit
--url-base base URL path prefix for commands, e.g. wd/url
--whitelisted-ips comma-separated whitelist of remote IPv4 addresses which are allowed to connect to Chr
omeDriver

chromedriver默认监听9515端口。

先来段简单的代码试试。获取WebView中显示的内容

# coding: utf-8
from selenium import webdriver

options = {'androidPackage': 'com.appium.android.apis', 'androidUseRunningApp': True}
d = webdriver.Remote("http://localhost:9515", {"chromeOptions": options})
# /html/body/a
el = d.find_element_by_xpath("/html/body/a")
print(el.text)

运行,之后就报错了,提示

WebDriverException: Message: unknown error: com.appium.android.apis is not installed on device 3578298f
(Driver info: chromedriver=2.41.578737 (49da6702b16031c40d63e5618de03a32ff6c197e),platform=Windows NT 10.0.16299 x86_6
4)

估计是通过pm path <package)name>判断的有没有安装,也罢,再安装一遍好了。然后给options加了androidProcess参数

options = {
'androidPackage': 'io.appium.android.apis',
'androidUseRunningApp': True,
'androidProcess': 'io.appium.android.apis'
}

重新再运行代码终于出来了

Hello World! - 1

哎,真是麻烦。。 -~~

续:分析chromedriver到底在做什么?

chromedriver有个参数--verbose可以输出更详细的信息。

./chromedriver --verbose

然后写段Python代码请求一下看看

from selenium import webdriver
from contextlib import contextmanager

@contextmanager
def driver(package_name):
capabilities = {
"androidDeviceSerial": "bf755cab",
"androidPackage": package_name,
"androidUseRunningApp": True,
}
dr = webdriver.Remote("http://localhost:9515", {
"chromeOptions": capabilities
})
try:
yield dr
finally:
dr.quit()

def main():
package_name = "io.appium.android.apis"
package_name = "com.xueqiu.android"

with driver(package_name) as dr:
print(dr.current_url)
#dr.save_screenshot("s.png")

if __name__ == "__main__":
main()

运行这段Python代码之后,你会看到chromedriver有一堆的输出。下面我把主要的代码贴出来

[17.612][DEBUG]: Sending adb command: host:devices
[17.612][DEBUG]: Sending adb command: host:transport:bf755cab|shell:pm path com.xueqiu.android
[17.930][DEBUG]: Received adb response: package:/data/app/com.xueqiu.android-1/base.apk
[17.930][DEBUG]: Sending adb command: host:transport:bf755cab|shell:ps
--- 这里还缺少获取 cat /proc/net/unix的步骤,log中看不到
[17.988][DEBUG]: Sending adb command: host-serial:bf755cab:forward:tcp:12604;localabstract:webview_devtools_remote_8495

--- 验证当前的Browser版本号是否在支持的范围内
[17.989][DEBUG]: DevTools request: http://localhost:12604/json/version
[17.992][DEBUG]: DevTools response: {
"Android-Package": "com.xueqiu.android",
"Browser": "Chrome/62.0.3202.84",
"Protocol-Version": "1.2",
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; SM901 Build/MXB48T; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36",
"V8-Version": "6.2.414.37",
"WebKit-Version": "537.36 (@957d898f0f6e46cd9661d91d2bae899f34c1c4b6)",
"webSocketDebuggerUrl": "ws://localhost:12604/devtools/browser"
}
--- 获取所有的tab
[17.992][DEBUG]: DevTools request: http://localhost:12604/json
[17.995][DEBUG]: DevTools response: [ {
"description": "{\"attached\":true,\"empty\":false,\"height\":1698,\"screenX\":0,\"screenY\":222,\"visible\":true,\"width\":1080}",
"devtoolsFrontendUrl": "http://chrome-devtools-frontend.appspot.com/serve_rev/@957d898f0f6e46cd9661d91d2bae899f34c1c4b6/inspector.html?ws=localhost:12604/devtools/page/82563818-74f8-4ed3-b6af-f294509fa0b6",
"faviconUrl": "https://assets.imedao.com/broker/static/images/favicon.8d2e0522.png",
"id": "82563818-74f8-4ed3-b6af-f294509fa0b6",
"title": "平安证券 极速开户",
"type": "page",
"url": "https://broker.xueqiu.com/open/pazq-l2-v1?snb_from=tab",
"webSocketDebuggerUrl": "ws://localhost:12604/devtools/page/82563818-74f8-4ed3-b6af-f294509fa0b6"
}]

---
[18.000][INFO]: resolved localhost to ["::1","127.0.0.1"] # 这个感觉多此一举
[18.004][DEBUG]: DEVTOOLS COMMAND Log.enable (id=1)
[18.004][DEBUG]: DEVTOOLS COMMAND DOM.getDocument (id=2)
[18.004][DEBUG]: DEVTOOLS COMMAND Runtime.enable (id=3)
[18.005][DEBUG]: DEVTOOLS COMMAND Page.enable (id=4)
[18.005][DEBUG]: DEVTOOLS COMMAND Page.enable (id=5)
[18.011][DEBUG]: DEVTOOLS EVENT Log.entryAdded {
"entry": {
"level": "warning",
"lineNumber": 0,
"source": "rendering",
"text": "The key \"viewport-fit\" is not recognized and ignored.",
"timestamp": 1568087651303.1,
"url": "https://broker.xueqiu.com/open/pazq-l2-v1?snb_from=tab"
}
}
[18.012][DEBUG]: DEVTOOLS RESPONSE DOM.getDocument (id=2) {
"root": {
"backendNodeId": 61,
"baseURL": "https://broker.xueqiu.com/open/pazq-l2-v1?snb_from=tab",
"childNodeCount": 2,
... 太多了,省略不写了
[18.012][DEBUG]: DEVTOOLS EVENT Runtime.executionContextCreated {
"context": {
"auxData": {
"frameId": "8495.4",
"isDefault": true
},
"id": 19,
"name": "",
"origin": "https://broker.xueqiu.com"
}
}
[18.015][DEBUG]: DEVTOOLS EVENT Runtime.consoleAPICalled {
"args": [ {
"type": "string",
"value": "{\"url\":\"/account/bind/show.json\",\"type\":\"GET\",\"data\":{},\"success\":\"cb1\",\"error\":\"cb2\",\"name\":\"request\"}"
} ],
"executionContextId": 19,
"stackTrace": {
"callFrames": [ {
"columnNumber": 2213,
"functionName": "render",
"lineNumber": 11,
"scriptId": "470",
"url": "https://assets.imedao.com/broker/static/js/open/pazq.96a2b2c8.js"
}
... 后面还很长,不写了
[18.031][DEBUG]: DEVTOOLS COMMAND Runtime.evaluate (id=8) {
"expression": "(function() { // Copyright (c) 2012 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n/**\n * Enum f...",
"returnByValue": true
}
[18.039][DEBUG]: DEVTOOLS RESPONSE Runtime.evaluate (id=8) {
"result": {
"type": "object",
"value": {
"status": 0,
"value": 1
}
}
}
--- 不知道哪里调用的
[18.039][INFO]: RESPONSE InitSession {
"acceptSslCerts": true,
"applicationCacheEnabled": false,
"browserConnectionEnabled": false,
"browserName": "chrome",
"chrome": {
"chromedriverVersion": "2.29.461585 (0be2cd95f834e9ee7c46bcc7cf405b483f5ae83b)"
},
"cssSelectorsEnabled": true,
"databaseEnabled": false,
"handlesAlerts": true,
"hasTouchScreen": true,
"javascriptEnabled": true,
"locationContextEnabled": true,
"mobileEmulationEnabled": false,
"nativeEvents": true,
"pageLoadStrategy": "normal",
"platform": "ANDROID",
"rotatable": false,
"takesHeapSnapshot": true,
"takesScreenshot": true,
"unexpectedAlertBehaviour": "",
"~~~": "..."
}

-- GetURL的逻辑 (很长,看的我头疼)
[18.054][DEBUG]: DEVTOOLS COMMAND Runtime.evaluate (id=11) {
"expression": "var isLoaded = document.readyState == 'complete' || document.readyState == 'interactive';if (isLoaded) { var frame = document.createElement('iframe'); frame.name = 'chromedriver dummy frame'; ..."
}
[18.108][DEBUG]: DEVTOOLS EVENT DOM.childNodeCountUpdated {
"childNodeCount": 4,
"nodeId": 10
}
[18.108][DEBUG]: DEVTOOLS EVENT Page.frameAttached {
"frameId": "8495.18",
"parentFrameId": "8495.4",
"stack": {
"callFrames": [ {
"columnNumber": 240,
"functionName": "",
"lineNumber": 0,
"scriptId": "478",
"url": ""
} ]
}
}
[18.108][DEBUG]: DEVTOOLS EVENT Page.frameStartedLoading {
"frameId": "8495.18"
}
[18.108][DEBUG]: DEVTOOLS EVENT Page.frameNavigated {
"frame": {
"id": "8495.18",
"loaderId": "8495.18",
"mimeType": "text/html",
"name": "chromedriver dummy frame",
"parentId": "8495.4",
"securityOrigin": "://",
"url": "about:blank"
}
}
[18.108][DEBUG]: DEVTOOLS EVENT Runtime.executionContextCreated {
"context": {
"auxData": {
"frameId": "8495.18",
"isDefault": true
},
"id": 26,
"name": "",
"origin": "https://broker.xueqiu.com"
}
}
[18.108][DEBUG]: DEVTOOLS EVENT Page.frameStoppedLoading {
"frameId": "8495.18"
}
[18.108][DEBUG]: DEVTOOLS RESPONSE Runtime.evaluate (id=11) {
"result": {
"description": "176",
"type": "number",
"value": 176
}
}
[18.108][INFO]: Done waiting for pending navigations. Status: ok
[18.108][DEBUG]: DEVTOOLS COMMAND Runtime.evaluate (id=12) {
"expression": "(function() { // Copyright (c) 2012 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n/**\n * Enum f...",
"returnByValue": true
}
[18.133][DEBUG]: DEVTOOLS EVENT DOM.childNodeCountUpdated {
"childNodeCount": 3,
"nodeId": 10
}
[18.133][DEBUG]: DEVTOOLS EVENT Runtime.executionContextDestroyed {
"executionContextId": 26
}
[18.133][DEBUG]: DEVTOOLS EVENT Page.frameDetached {
"frameId": "8495.18"
}
[18.136][DEBUG]: DEVTOOLS RESPONSE Runtime.evaluate (id=12) {
"result": {
"type": "object",
"value": {
"status": 0,
"value": "https://broker.xueqiu.com/open/pazq-l2-v1?snb_from=tab"
}
}
}
[18.136][INFO]: Waiting for pending navigations...
[18.136][DEBUG]: DEVTOOLS COMMAND Runtime.evaluate (id=13) {
"expression": "1"
}
[18.141][DEBUG]: DEVTOOLS RESPONSE Runtime.evaluate (id=13) {
"result": {
"description": "1",
"type": "number",
"value": 1
}
}
[18.141][INFO]: Done waiting for pending navigations. Status: ok
[18.141][INFO]: RESPONSE GetUrl "https://broker.xueqiu.com/open/pazq-l2-v1?snb_from=tab"

参考

附言 1  ·  June 11, 2019

今天试了下微信小程序,发现不能用 应用内打开 debugx5.qq.com 开启webview调试了。不过发现使用Xposed强制依然有效。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 15 条回复 时间 点赞

你快肥来~ 定位下微信切webview切不过去的问题,是chromedriver的bug吗,atx可以代理到ps的命令吗,如果可以能不能处理一下

是啊,我也是这个问题,大神快回来写

同版本的chomedriver 已经饥渴难耐

@seveniruby 大佬帮看看 ,你webview最熟悉了

思寒_seveniruby 将本帖设为了精华贴 30 Aug 19:45

技术贴 收藏先 慢慢看 多谢楼主

@BensonMax 我在6.0和7.0可以打印出page_source了,这应该算是切过去了吧;8.0上的是chromedriver ps命令的bug,之前听思寒@seveniruby 讲课说过,目前还没能力弄个adb proxy。。@codeskyblue作者来搞一下

driver = ChromeDriver(d).driver(process='com.tencent.mm:toolsmp')  #我的进程是这个,另外最好从搜一搜进小程序,因为我直接进小程序,发现chrome://inspect没有webview
time.sleep(3)
print(driver.page_source)

这个思路很好,很棒

Jacc 回复

这有何难

codeskyblue 回复

👏 期待新版本😀

有办法 像atx-agent,在手机上起个http,去连接chrome_devtools_remote这个unix domain socket,然后发送请求完成web测试么?这样就不用adb了

蓝眼墨 回复

但是chromedriver需要adb呀,或者说不用chromedriver,直接基于chrome_devtools开搞

codeskyblue 回复

chromedriver 就是用 adb forward tcp:9222 localabstract:chrome_devtools_remote映射这个socket 然后发送http请求的,直接和chrome_devtools_remote交互就不用过chromedriver

蓝眼墨 回复

直接和chrome_devtools_remote交互就不用过chromedriver这个你实现了么

17Floor has been deleted

没遇到过哎

19Floor has been deleted

Hello World! - 1 出来了,可是小程序还是出不来,为甚么

simple 专栏文章:[精华帖] 社区历年精华帖分类归总 中提及了此贴 13 Dec 14:44
codeskyblue 专栏文章:2018年 终总结 中提及了此贴 18 Feb 10:26
linpengcheng 基于 ATX-Server 的 UI 自动化测试框架 中提及了此贴 13 May 17:40
lan-tianyu [Topic was deleted] 中提及了此贴 21 Jun 15:46
codeskyblue webview 研究踩到的坑 中提及了此贴 10 Sep 11:42
codeskyblue webview 研究踩到的坑 中提及了此贴 11 Sep 10:47
需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up