ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Playwright Android 自动化完全指南:AndroidDevice API 详解与 ADB 驱动实现原理

Playwright Android 自动化完全指南:AndroidDevice API 详解与 ADB 驱动实现原理 Playwright Android 自动化完全指南AndroidDevice API 详解与 ADB 驱动实现原理【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwrightPlaywright 自 v1.9 起提供了实验性的 Android 自动化支持其核心抽象是AndroidDevice类它代表一台通过 ADB 连接的真实设备或模拟器AVD既能对原生 UI 控件做点击、滑动、填写等交互也能接管设备上的 Chrome 浏览器与 WebView让同一套Page/BrowserContextAPI 直接运行在移动端。读完本篇你将掌握 AndroidDevice 的全部方法签名与参数默认值、从 ADB 连接到浏览器接管的完整实战流程以及从源码层面看懂 Playwright 如何在设备上安装驱动 APK、通过本地抽象 socket 与 WebView 建立 DevTools 通道的底层机制。一、AndroidDevice 是什么概念、前提与获取方式根据官方 API 文档 class-androiddevice.mdAndroidDevice表示一台已连接的设备真机或模拟可通过method: Android.devices获取。它继承自事件源核心事件有两个事件载荷触发时机closeAndroidDevice设备连接关闭时v1.28 起webViewAndroidWebView检测到新的 WebView 实例时运行 Android 自动化前需要满足以下前提引自 class-android.md一台 Android 真机或 AVD 模拟器正在运行并与设备完成认证的ADB daemon——通常执行一次adb devices即可设备上安装了 Chrome 87 或更高版本在 Chrome 的chrome://flags中开启 Enable command line on non-rooted devices这是 Playwright 能给 Chrome 传--remote-debugging-socket-name等启动参数的前提。设备发现由Android.devices()完成支持指定远程 ADB server 的host默认127.0.0.1与port默认5037以及omitDriverInstall跳过每次连接时自动安装驱动 APK见第五节。多设备场景下还可以用Android.connect(endpoint)v1.28 起连接到Android.launchServer()启动的服务器实例launchServer的 WebSocket 默认只监听localhost且wsPath默认是一个不可猜测的随机串——文档明确警告任何知道wsPath的进程都可能接管 OS 用户权限因此显式指定wsPath时必须使用不可猜测的 token。设备连接建立后的第一步通常是最基础的三个只读方法const { _android: android } require(playwright); (async () { // 获取所有已连接的 Android 设备 const [device] await android.devices(); console.log(Model: ${device.model()}); // 设备型号 console.log(Serial: ${device.serial()}); // 设备序列号 await device.screenshot({ path: device.png }); // 整机截图 await device.close(); })();model()返回设备型号。从源码 android.ts 看它在设备初始化时执行shell:getprop ro.product.model获取因此这是真实的系统属性值。serial()返回设备序列号即 ADB 层识别设备的唯一标识。screenshot()返回截图Buffer可选path参数落盘相对路径基于当前工作目录。服务端实现就是执行shell:screencap -p见 android.ts所以截图是 PNG 格式且覆盖整个屏幕。文档同时提醒设备必须处于唤醒状态才能出图建议开启开发者模式的 Stay awake。二、AndroidDevice 完整 API 参考以下按功能域整理 class-androiddevice.md 中的全部方法。所有标注timeout的方法共享同一套超时语义默认 30 秒可用device.setDefaultTimeout(ms)修改该设置在设备对象级别会覆盖Android.setDefaultTimeout的全局默认值传0关闭超时。2.1 控件交互方法都接收 AndroidSelectorAndroidSelector是匹配原生控件的选择器对象字段包括res资源 id、text、desccontent description、pkg、clazz、checkable/checked/clickable/enabled/focusable/focused/longClickable/scrollable/selected等布尔状态、depth以及结构查询hasChild: { selector }与hasDescendant: { selector, maxDepth }。字符串形式的字段值在客户端会被编译为正则详见第六节。方法签名要点说明tap(selector, opts?)opts: { duration?, timeout? }点击控件。duration毫秒为可选按压时长longTap(selector, opts?)opts: { timeout? }长按控件fill(selector, text, opts?)opts: { timeout? }清空并填入文本目标须是输入框press(selector, key, opts?)key: AndroidKey在控件上下文中按键。客户端实现是tap(selector)后调用input.press(key)见 android.tsswipe(selector, direction, percent, opts?)direction: down|up|left|right按指定方向滑动percent为相对控件尺寸的距离百分比scroll(selector, direction, percent, opts?)同上滚动控件作用于可滚动元素fling(selector, direction, opts?)opts: { speed?, timeout? }快速甩动控件speed单位是像素/秒drag(selector, dest, opts?)dest: { x, y }将控件拖拽到目标坐标点pinchOpen(selector, percent, opts?)opts: { speed?, timeout? }按放大方向捏合percent为相对控件尺寸的比例pinchClose(selector, percent, opts?)同上按缩小方向捏合除drag目标是绝对坐标{x, y}外其余方法都以AndroidSelector定位控件speed像素/秒可选参数决定手势速度缺省时由驱动端使用默认速度。2.2 等待与查询方法签名要点说明wait(selector, opts?)opts: { state?: gone, timeout? }等待控件出现state: gone时等待控件消失info(selector)返回AndroidElementInfo返回控件的文本、描述、资源 id、包名、类名等属性是调试选择器的重要手段waitForEvent(event, optionsOrPredicate?)事件名如webview等待事件并传入谓词谓词返回 truthy 时 resolve默认超时 30000mswebViews()返回AndroidWebView[]当前已打开的 WebView 列表webView(selector, opts?)selector: { pkg?, socketName? }等待匹配pkg或socketName的 WebView 打开并返回AndroidWebView。客户端实现android.ts是先在本地已缓存的 WebView 集合中查找找不到则挂起等待webview事件——事件由服务端每 500ms 轮询一次 Unix socket 列表产生见第五节2.3 设备级操作方法签名要点说明shell(command)返回Buffer在设备上执行 shell 命令并返回输出。所有命令在服务端加shell:前缀经 ADB 执行open(command)返回AndroidSocket启动 shell 进程并返回可读写 socketwrite/close以及data/close事件适合需要双向流的场景如open(localabstract:playwright_android_driver_socket)installApk(file, opts?)file: string \| Bufferopts: { args? }安装 APKfile可以是本地路径或文件内容。args是传给cmd package install的参数默认-r -t -S覆盖安装、允许测试包、静默。服务端实现通过 ADB socket 把 APK 字节流直接写入cmd package install args length通道见 android.ts无需先推文件push(file, path, opts?)opts: { mode? }把文件拷贝到设备。mode可选默认644rw-r--r--。从源码看它使用的是 ADB sync 协议打开sync:socket 后按SEND/DATA65535 字节分块/DONE三段发送并等待OKAY应答见 android.tsscreenshot(opts?)opts: { path? }返回Buffer整机截图见第一节launchBrowser(opts?)返回BrowserContext在设备上启动 Chrome或pkg指定的其他浏览器并返回其持久化上下文。除pkg外还接受标准 BrowserContext 参数v1.8 起的共享 context 参数列表以及proxy、argsv1.29 起close()—断开设备连接触发close事件时也会清理所有已建立的浏览器连接input属性类型AndroidInput低级输入通道type(text)、press(key)、tap(point)、swipe(from, segments, steps)、drag(from, to, steps)直接以坐标/分段方式注入输入不依赖控件选择器setDefaultTimeout(timeout)毫秒修改该设备下所有接受timeout的方法的默认超时2.4 launchBrowser 的底层流程launchBrowser()是整个 Android API 中最复杂的调用。从服务端源码 android.ts 可以看到完整链路am force-stop pkg先杀掉目标浏览器默认com.android.chrome生成一个唯一的 socket 名playwright_guid_devtools_remote测试模式下为固定名webview_devtools_remote_playwright_test组装 Chrome 启动参数--disable-fre、--no-default-browser-check、--remote-debugging-socket-namesocketName、Android 专用 Chromium 开关以及用户传入的proxy会翻译成--proxy-server/--proxy-bypass-list与args命令行的特殊字符容易在 shell 中出问题所以源码把它 base64 编码后写入设备上的/data/local/tmp/chrome-command-line再用am start -a android.intent.action.VIEW -d about:blank pkg拉起浏览器Chrome 的 command-line 文件机制会读取该文件通过open(localabstract:socketName)打开这个 DevTools 抽象 socket手工完成一次 HTTP Upgrade 握手把 socket 包装成AndroidBrowser内置 WebSocket 收发器随后以persistent持久化上下文模式连接CRBrowser返回默认BrowserContext成功后删除临时命令行文件失败则关闭已建立的上下文并抛错。这也解释了为什么文档要求开启 Enable command line on non-rooted devicesChrome 只有在允许命令行覆盖时才会读取注入的调试 socket 参数。三、完整实战从连接设备到自动化 Chrome 与 WebView下面是官方文档class-android.md给出的端到端示例覆盖了model/serial/screenshot/shell/webView/fill/press/launchBrowser/close等主力 APIconst { _android: android } require(playwright); (async () { // 连接设备。 const [device] await android.devices(); console.log(Model: ${device.model()}); console.log(Serial: ${device.serial()}); // 对整个设备截图。 await device.screenshot({ path: device.png }); { // --------------------- WebView 自动化 ----------------------- // 启动一个带 WebView 的应用。 await device.shell(am force-stop org.chromium.webview_shell); await device.shell(am start org.chromium.webview_shell/.WebViewBrowserActivity); // 获取 WebView。 const webview await device.webView({ pkg: org.chromium.webview_shell }); // 填充地址输入框。 await device.fill({ res: org.chromium.webview_shell:id/url_field, }, github.com/microsoft/playwright); await device.press({ res: org.chromium.webview_shell:id/url_field, }, Enter); // 像普通 Page 一样操作 WebView 里的页面。 const page await webview.page(); await page.waitForURL(/.*microsoft\/playwright.*/); console.log(await page.title()); } { // --------------------- Chrome 浏览器自动化 ----------------------- // 启动 Chrome。 await device.shell(am force-stop com.android.chrome); const context await device.launchBrowser(); // 像普通 BrowserContext 一样使用。 const page await context.newPage(); await page.goto(https://webkit.org/); console.log(await page.evaluate(() window.location.href)); await page.screenshot({ path: page.png }); await context.close(); } // 关闭设备连接。 await device.close(); })();注示例中地址栏选择器org.chromium.webview_shell:id/url_field演示了 Android 选择器的res字段用法fill之后用press(..., Enter)提交。AndroidWebView.page()会把 WebView 适配为标准的Page对象客户端实现见 android.ts通过connectToWebView在 socket 上建立 DevTools 连接并取到上下文中的第一个页面此后page.goto/page.title等 API 与桌面端完全一致。原文档示例中使用的page.waitForNavigation是旧版 API新版可等价写作page.waitForURL。AndroidWebView对象本身也提供三个属性方法pid()宿主进程 PID、pkg()宿主包名、以及内部使用的 socket 名配合device.webViews()可以枚举当前所有 WebView配合device.on(webView, ...)事件可以在应用内 WebView 打开的瞬间做出响应。3.1 跨进程launchServer / connect 模式当 ADB 与测试进程不在同一台机器例如 CI 节点连测试机上的 ADB serverv1.28 起的 server/client 模式更合适。服务端const { _android } require(playwright); (async () { const browserServer await _android.launchServer({ // 多台设备连接、想固定使用其中一台时 // deviceSerialNumber: deviceSerialNumber, }); const wsEndpoint browserServer.wsEndpoint(); console.log(wsEndpoint); })();客户端const { _android } require(playwright); (async () { const device await _android.connect(wsEndpoint); console.log(device.model()); console.log(device.serial()); await device.shell(am force-stop com.android.chrome); const context await device.launchBrowser(); const page await context.newPage(); await page.goto(https://webkit.org/); console.log(await page.evaluate(() window.location.href)); await page.screenshot({ path: page-chrome-1.png }); await context.close(); })();关键选项launchServer接受adbHost/adbPort指定 ADB server默认127.0.0.1:5037、deviceSerialNumber多设备时必须显式指定否则抛错、hostv1.45 起默认localhost显式传0.0.0.0会把设备 RPC 暴露到网络、port默认0随机端口、wsPath默认不可猜测的随机串和omitDriverInstall。connect(endpoint, options?)侧则支持headers、slowMo毫秒级减速便于观察与timeout默认 30000ms0表示禁用。从客户端源码 android.ts 看连接时会自动带上x-playwright-browser: android头并在握手后校验 endpoint 是否由launchServer产生不是则报 Malformed endpoint。四、AndroidSelector 是怎么匹配的客户端正则编译AndroidDevice的所有控件方法都接收AndroidSelector而它的字符串字段在发往服务端之前会在客户端被统一编译成正则表达式见 toSelectorChannel传入RegExp时直接取其source即你写的正则原样生效传入字符串时所有正则特殊字符|\\{}()[\]^$*?.被转义并整体包上^...$锚点——字符串值按精确全匹配处理例如res: com.app:id/login_btn只匹配该完整资源 id而不是子串hasChild/hasDescendant会递归做同样的编译hasDescendant额外携带maxDepth限制向下搜索深度布尔字段clickable、enabled、selected等与depth则原样透传。这个细节直接影响选择器编写想以 login 开头要写res: /^com\.app:id\/login/而不是res: com.app:id/login。协议层的完整方法清单定义在 android.yml可用于核对每个方法对应的 RPC 名称与参数。五、源码深读Playwright 如何驱动一台 Android 设备理解了 API 表面之后真正有趣的是服务端packages/playwright-core/src/server/android/android.ts如何在 ADB 之上构建出一套可靠的能力。5.1 设备发现与 ADB 抽象Android.devices()调用后端Backend.devices()ADB 实现的adb devices语义过滤出status device的条目并按序列号增量维护一个serial - AndroidDevice映射新序列号创建设备对象消失的序列号则从映射中移除android.ts。设备创建时会执行shell:getprop ro.product.model读型号。AndroidDevice的shell/screenshot/open全部构建在两个后端原语上runCommand(command)执行shell:xxx形式的 ADB 命令并拿回输出open(command)打开一条长连接 socket例如shell:cmd package install ...、localabstract:name、sync:。AndroidDevice.shell()每次执行完命令后还会立即刷新一次 WebView 列表_refreshWebViews保证am start之类命令之后能尽快发现新 WebView。5.2 驱动 APK控件交互的真正执行者tap/fill/swipe等 UI 交互不直接走 ADB shell而是走设备上安装的驱动 APK。首次需要交互时_installDriver()android.ts会am force-stop com.microsoft.playwright.androiddriver停掉旧驱动若未设置omitDriverInstall先cmd package uninstall两个驱动包androiddriver与androiddriver.test再从 Playwright 安装目录读取android-driver.apk与android-driver-target.apk用与installApk相同的 socket 通道装上去文件缺失时提示执行playwright install androidam instrument -w com.microsoft.playwright.androiddriver.test/androidx.test.runner.AndroidJUnitRunner启动 instrument 进程轮询localabstract:playwright_android_driver_socket直到可连接包装成 JSON-RPC 式通道每条消息是{ id, method, params }服务端按id匹配挂起的 Promiseerror字段则触发 reject见 _send。从源码结构看UI 动作tap、swipe、pinch 等本质上是发给驱动 APK 的 RPC 调用由 APK 内的 instrumentation 框架完成手势合成——这也意味着驱动 APK 版本与 Playwright 版本需要配套默认每次连接都会重装以保证一致CI 中确认驱动已就位时可用devices({ omitDriverInstall: true })跳过这段安装开销。5.3 WebView 是怎么被看见的device.webView()/webViews()的数据来自_refreshWebViews()android.ts每 500ms 执行shell:cat /proc/net/unix | grep webview_devtools_remote扫描系统 Unix socket 表用正则提取webview_devtools_remote_pid[name]形式的 socket 名并从 socket 名中解析出宿主进程 PID再用ps -A | grep pid反查出包名与本地缓存比对新出现的 socket 触发webViewAdded事件客户端即device.on(webView, ...)的来源消失的触发webViewRemoved。所以检测到新 WebView完全是对/proc/net/unix的轮询结果webView({ pkg, socketName })的匹配键也由此而来waitForEvent的默认 30 秒超时覆盖了轮询发现的延迟。5.4 连接链路与关闭语义客户端AndroidDevice对象与设备之间是标准的 Playwright 通道协议dispatcher 见 androidDispatcher.ts而close的语义值得注意device.close()在服务端会停止 WebView 轮询、关闭所有浏览器连接包括由launchBrowser/ WebView 建立的 DevTools 通道、reject 所有未决的驱动 RPC、关闭驱动 socket 并断开 ADB 会话最后向客户端广播close事件android.ts。在connect()模式下客户端还会把close与 WebSocket 连接绑定_shouldCloseConnectionOnClose设备掉线即断开 RPC 连接避免悬挂状态。六、已知限制、排错与延伸阅读结合文档声明与源码行为实践时的主要限制是必须有 ADB原始 USB 通信尚不支持一切能力都构建在 ADB daemon 之上adb devices是最基本的健康检查截图要求设备唤醒锁屏状态下screenshot()可能失败或返回黑屏建议开启 Stay awakeChrome 版本门槛launchBrowser依赖 Chrome 的命令行文件机制与自定义 remote debugging socket需要 Chrome 87 且开启对应 flag驱动安装开销默认每次连接都卸载重装两个驱动 APKCI 环境可评估omitDriverInstall实验性定位官方文档明确标注 Android 支持为 experimental并非所有测试都在真机上跑过遇到个别方法异常属于已知状态。排错手段上device.info(selector)可以先确认选择器命中了什么控件返回AndroidElementInfodevice.shell(logcat -d | tail -n 100)一类命令可用于查看设备日志DEBUGpw:android可打开驱动安装与 socket 连接的调试日志源码中的debug(pw:android)埋点截图设备级screenshot()与页面级page.screenshot()则是视觉回归的直接依据。仓库中与 Android 自动化相关的入口便于继续深入资源路径AndroidDevice API 参考docs/src/mobile-api/class-androiddevice.mdAndroid 总览与示例docs/src/mobile-api/class-android.mdAndroidInput / AndroidSocket / AndroidWebView 参考class-androidinput.md、class-androidsocket.md、class-androidwebview.md客户端 API 实现packages/playwright-core/src/client/android.ts服务端 ADB/驱动实现packages/playwright-core/src/server/android/android.ts驱动 APK 工程packages/playwright-core/src/server/android/driver协议规范packages/protocol/spec/android.yml集成测试tests/android/android.spec.ts、tests/android/device.spec.ts、tests/android/browser.spec.ts、tests/android/launch-server.spec.ts至此AndroidDevice的全部方法、参数默认值与底层实现路径都已覆盖从 ADB 连接、驱动 APK 安装、Unix socket 上的 WebView 发现到 Chrome 命令行注入与 DevTools WebSocket 升级每一层都可以对照仓库源码验证。【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表