新闻详情

新闻详情

首页 / 资讯中心 / 详情

AirTest+ADB 解决 vivo APK 批量自动安装验证弹窗

发布时间:2026/10/1 9:06:49来源:尧图网络
AirTest+ADB 解决 vivo APK 批量自动安装验证弹窗
APK 自动安装这件事看起来只是把文件推到手机里然后点几下但真到了批量设备上跑自动化的时候各种系统层的确认弹窗能把流程卡得死死的。vivo 手机在这方面的表现尤其典型安装验证环节多、弹窗样式不统一、部分机型还有额外的安全扫描提示手动点一次两次还行几百台设备跑起来就是灾难。我用 AirTest 配合 ADB 做了一套自动安装方案把 vivo 的安装验证环节拆解成可识别的界面元素用图像识别加控件定位双保险的方式绕过去实测在 X100、Y78、S18 这几条产品线上都能稳定跑通。这套东西适合做 App 兼容性测试、设备农场搭建、批量装机脚本的同行参考不需要太深的 Android 底层知识但得对 ADB 和 AirTest 的基本操作有个概念。1. 方案整体设计与核心思路拆解1.1 为什么选 AirTest 而不是纯 ADB 脚本纯 ADB 命令安装 APK 就一行adb install简单直接。但 vivo 手机在adb install之后会弹出一个系统级的安装验证界面要求用户点击“继续安装”或者输入锁屏密码这个弹窗不走 ADB 的返回通道adb install命令会一直挂起等待直到超时失败。用-g参数或者-r参数都跳不过去因为这是系统 UI 层的拦截不是 ADB 协议层能控制的。AirTest 的优势在于它能直接操作屏幕。它封装了 Android 的 UIAutomator 和图像识别两套引擎可以在 APK 推送完成后实时检测屏幕上出现了什么然后模拟点击。你不需要去逆向 vivo 的安装器代码也不需要 hook 系统服务只要让程序“看到”那个按钮然后点下去就行。这个思路的好处是通用性强不同 vivo 机型、不同系统版本之间弹窗样式变来变去但核心按钮的文字和位置特征基本稳定调整成本低。另一个考虑是 AirTest 的跨设备兼容性。它基于 Python可以很方便地写成批处理脚本一台设备跑通了改个设备序列号就能复制到下一台。配合poco或者原生touch接口代码量不大维护起来也轻松。1.2 整体流程设计整个自动安装流程分四个阶段设备准备、APK 推送、安装验证处理、结果确认。设备准备阶段要确保 ADB 连接稳定、屏幕常亮、锁屏解除。vivo 手机有个特点息屏状态下安装验证弹窗可能不会正常弹出或者弹出了但屏幕不亮导致图像识别截到黑屏。所以我会在脚本开头强制唤醒屏幕并保持常亮。APK 推送阶段用adb push把文件传到/data/local/tmp/目录然后调用pm install命令。这里有个细节pm install在部分 vivo 机型上会触发“安装未知来源应用”的权限确认如果之前没给 ADB 授权会直接失败。所以我会在推送前先检查并授权。安装验证处理是核心环节。pm install执行后会阻塞此时 AirTest 启动一个并行线程去轮询屏幕检测是否有安装验证界面出现。一旦匹配到预设的图像模板或者控件特征就执行点击操作。vivo 的验证界面通常有两到三层第一层是“继续安装”按钮第二层是“安装”确认部分机型还有“安全检测”的进度条需要等待进度条走完再点最终确认。结果确认阶段用pm list packages检查目标包名是否出现在已安装列表里同时用 AirTest 截图存档方便出问题时回溯。1.3 关键难点与应对策略vivo 安装验证最麻烦的地方在于弹窗样式不统一。我遇到过几种典型情况X100 上是底部弹出卡片按钮是“继续安装”Y78 上是全屏白色背景按钮是“安装”S18 上则是半透明遮罩按钮文字是“允许”。如果只用一种图像模板去匹配换台设备就失效了。我的应对策略是“文字识别优先图像匹配兜底”。AirTest 支持poco的文本定位可以用text(继续安装)这种方式直接找按钮。但 vivo 的安装器界面有时候不是标准 Android 控件poco抓不到节点这时候就退回到图像识别截取按钮区域的截图作为模板。为了减少模板数量我会把按钮区域截取成小图然后用Template类设置一个较低的阈值比如 0.7这样不同分辨率下也能匹配上。还有一个坑是弹窗出现的时机。pm install命令是阻塞的但弹窗弹出的时间点不确定有时候 1 秒有时候 5 秒。如果轮询间隔太长可能错过太短又浪费 CPU。我实测下来200 毫秒轮询一次配合 30 秒超时是比较稳妥的平衡点。另外vivo 的安装验证有时会要求输入锁屏密码。这个情况在自动化场景下比较棘手我的处理方式是提前在设备设置里关闭“安装应用需要验证”的选项或者用 ADB 命令settings put global verifier_verify_adb_installs 0关掉 ADB 安装验证。这个命令在大部分 vivo 机型上有效但部分系统版本会重置所以脚本里每次安装前都会重新执行一遍。2. 环境准备与核心细节解析2.1 AirTest 环境搭建与设备连接AirTest 的安装有两种方式用官方 IDE 或者纯 Python 库。做自动化脚本我推荐直接用 Python 库方便集成到 CI 流程里。安装命令很简单pip install airtest pip install pocoui装完之后需要配置 Android SDK 环境变量主要是ANDROID_HOME和PATH里加上platform-tools。AirTest 底层依赖 ADB所以 ADB 版本不能太老建议用 1.0.41 以上。连接 vivo 设备有个小细节。vivo 手机默认的 USB 调试模式是“仅充电”插上线之后需要在开发者选项里手动改成“传输文件”或者“MIDI”。部分机型还会弹出一个“是否允许 USB 调试”的授权框需要勾选“始终允许”。这些操作做一次就行后面脚本跑起来就不用管了。设备连接用 AirTest 的connect_device接口from airtest.core.api import connect_device device connect_device(Android:///)如果有多台设备可以用Android:///设备序列号的方式指定。序列号用adb devices命令查看。连接成功后AirTest 会返回一个设备对象后续所有操作都基于这个对象。有个坑要注意vivo 手机在开发者选项里有个“USB 调试安全设置”这个选项如果打开ADB 安装 APK 会要求输入验证码。做自动化之前一定要把它关掉。这个选项藏得比较深在“开发者选项”最底部不仔细找容易漏。注意每次系统更新后vivo 可能会重置开发者选项里的部分设置建议脚本启动时加一段检查逻辑用adb shell settings get global命令确认关键开关的状态。2.2 vivo 安装验证机制的技术背景要绕过验证得先搞清楚 vivo 在安装环节做了什么。Android 原生的安装流程是PackageManager服务收到安装请求然后启动PackageInstaller界面让用户确认。vivo 在这个基础上加了自己的PackageInstaller实现增加了几个拦截点第一个拦截点是“安装来源验证”。vivo 的系统会检查 APK 的来源如果是通过 ADB 安装的会标记为“未知来源”然后弹窗让用户确认。这个弹窗在原生 Android 上也有但 vivo 把它做得更强制默认没有“不再提示”的选项。第二个拦截点是“安全检测”。vivo 内置了一个安全扫描引擎安装前会快速扫描 APK 的签名和权限如果发现敏感权限或者签名不匹配会弹出额外的警告。这个检测在联网状态下更严格所以部分自动化脚本会选择在飞行模式下安装。第三个拦截点是“安装确认”。即使前面都过了最后还有一个“安装”按钮要点。这个按钮的控件 ID 在不同系统版本里不一样用 UIAutomator dump 抓出来的结构也不一致所以图像识别在这时候更可靠。理解这些拦截点的意义在于你可以有针对性地设计绕过策略。比如安全检测那个环节如果 APK 本身是正规签名的扫描会很快基本不影响流程。但如果 APK 有敏感权限扫描可能持续十几秒这时候脚本就得加等待逻辑不能急着点下一步。2.3 ADB 精简列表与关键命令vivo 手机预装应用多ADB 命令执行时如果碰到系统权限限制容易被拦截。我在实操中整理了一份精简的 ADB 命令列表只保留安装流程必需的部分减少不必要的系统调用命令用途备注adb devices检查设备连接确保设备状态为 deviceadb shell settings put global verifier_verify_adb_installs 0关闭 ADB 安装验证每次安装前执行adb shell settings put global package_verifier_enable 0关闭包验证部分机型需要adb push xxx.apk /data/local/tmp/推送 APK路径不要选 /sdcardadb shell pm install -r -t /data/local/tmp/xxx.apk执行安装-r 覆盖安装-t 允许测试包adb shell pm list packagesgrep 包名验证安装结果adb shell rm /data/local/tmp/xxx.apk清理临时文件避免占用空间这份列表里verifier_verify_adb_installs和package_verifier_enable这两个设置是绕过 vivo 验证的关键。实测在 X100 和 S18 上执行完这两条命令后pm install的弹窗会减少到只有一层“继续安装”按钮有时候甚至直接静默安装成功。Y78 上稍微顽固一点还是会弹窗但点击起来比之前顺畅。提示settings put命令写入的值在设备重启后会失效所以脚本里每次跑之前都要重新执行不要指望设一次就永久生效。另外pm install的路径一定要用/data/local/tmp/不要用/sdcard/。后者在部分 vivo 机型上会因为文件权限问题导致安装失败报错信息是Failure [INSTALL_FAILED_INVALID_URI]排查起来很浪费时间。3. 实操过程与核心环节实现3.1 设备初始化与 APK 推送脚本先写设备初始化部分。这段代码的目标是把设备状态调整到适合自动安装的模式from airtest.core.api import * import subprocess import time def init_device(serial): 初始化设备关闭验证开关保持屏幕常亮 adb_cmd fadb -s {serial} shell # 关闭 ADB 安装验证 subprocess.run(adb_cmd settings put global verifier_verify_adb_installs 0, shellTrue) subprocess.run(adb_cmd settings put global package_verifier_enable 0, shellTrue) # 保持屏幕常亮 subprocess.run(adb_cmd svc power stayon true, shellTrue) # 唤醒屏幕 subprocess.run(adb_cmd input keyevent KEYCODE_WAKEUP, shellTrue) # 解锁屏幕如果有滑动锁 subprocess.run(adb_cmd input swipe 500 1500 500 500, shellTrue) time.sleep(1) print(f设备 {serial} 初始化完成)这段代码里svc power stayon true是让屏幕在充电时保持常亮配合 USB 连接正好适用。滑动解锁那个命令是通用写法如果你的设备有密码或者图案锁需要改成对应的输入逻辑或者直接用 AirTest 的swipe接口操作。APK 推送部分def push_apk(serial, apk_path, remote_path/data/local/tmp/target.apk): 推送 APK 到设备临时目录 push_cmd fadb -s {serial} push {apk_path} {remote_path} result subprocess.run(push_cmd, shellTrue, capture_outputTrue, textTrue) if result.returncode ! 0: print(f推送失败: {result.stderr}) return False print(fAPK 已推送到 {remote_path}) return True推送完成后可以用adb shell ls -l /data/local/tmp/target.apk确认文件大小是否正常。有时候 USB 连接不稳定推送会中断文件大小对不上这时候直接重推就行。3.2 安装执行与验证弹窗处理这是整个流程的核心部分。pm install命令用subprocess.Popen异步执行不阻塞主线程然后主线程启动 AirTest 的轮询逻辑import threading def install_apk_async(serial, remote_path): 异步执行安装命令 install_cmd fadb -s {serial} shell pm install -r -t {remote_path} process subprocess.Popen(install_cmd, shellTrue, stdoutsubprocess.PIPE, stderrsubprocess.PIPE) return process def handle_install_dialog(serial, timeout30): 轮询处理安装验证弹窗 start_time time.time() # 定义可能的按钮特征 button_texts [继续安装, 安装, 允许, 确定] while time.time() - start_time timeout: # 方式一尝试文本匹配 for text in button_texts: if exists(Template(rtpl_button.png, threshold0.7)): touch(Template(rtpl_button.png, threshold0.7)) print(f点击了按钮: {text}) time.sleep(1) break # 方式二尝试图像匹配兜底 # 这里用截图模板匹配实际使用需要提前截取按钮图片 time.sleep(0.2) print(弹窗处理超时或已完成)实际代码里我会把文本匹配和图像匹配结合起来。文本匹配优先因为不需要维护模板图片图像匹配作为兜底应对控件抓不到的情况。图像模板的截取方法在 vivo 手机上手动触发一次安装弹窗出现时用 AirTest IDE 的截图工具截取按钮区域。注意只截按钮本身不要截整个弹窗这样匹配范围小、速度快、准确率高。截取的图片保存为tpl_continue.png这样的命名放在脚本同级目录。threshold0.7这个阈值是我实测下来比较合适的。设太高了不同分辨率下容易匹配失败设太低了可能误匹配到其他相似元素。如果你的设备分辨率跨度大比如从 1080P 到 2K可以准备两套模板按分辨率切换。还有一个细节vivo 的安装弹窗有时候是两层第一层点完之后第二层才出来。所以轮询逻辑不能只跑一次要在超时时间内持续检测每次点击后重置计时器。我通常设 30 秒总超时单次点击后等 1 秒再继续检测。3.3 安装结果验证与异常处理安装命令执行完后用pm list packages验证def verify_install(serial, package_name): 验证 APK 是否安装成功 check_cmd fadb -s {serial} shell pm list packages | grep {package_name} result subprocess.run(check_cmd, shellTrue, capture_outputTrue, textTrue) if package_name in result.stdout: print(f安装成功: {package_name}) return True else: print(f安装失败: {package_name} 未找到) return False包名可以从 APK 文件里提取用aapt dump badging xxx.apk | grep package命令或者用 Python 的apkutils库解析。异常处理方面我遇到过几种典型失败INSTALL_FAILED_INSUFFICIENT_STORAGE存储空间不足、INSTALL_FAILED_UPDATE_INCOMPATIBLE签名冲突、INSTALL_FAILED_VERSION_DOWNGRADE版本降级。前两种需要在脚本里加检查逻辑存储空间用adb shell df /data查签名冲突就加-d参数允许降级安装。版本降级用-d参数也能解决。对于超时的情况脚本里要加一个 kill 逻辑把挂起的pm install进程杀掉然后重试一次。重试次数设 2 次就够了再多说明设备本身有问题需要人工介入。4. 常见问题与排查技巧实录4.1 弹窗识别失败的原因排查弹窗识别失败是最常见的问题表现是脚本跑完超时了但 APK 其实已经装上了只是没点到按钮。排查思路按优先级来先看截图。AirTest 的exists和touch接口都支持传snapshot参数把识别时的屏幕截图保存下来。看截图里弹窗到底长什么样按钮位置在哪里和你预设的模板差异有多大。很多问题看一眼截图就清楚了。再看分辨率。如果模板是在 1080P 设备上截的拿到 2K 屏上匹配阈值又设得高大概率匹配不上。解决办法是用Template的resolution参数指定模板的原始分辨率AirTest 会自动缩放适配。还有一个容易忽略的点是屏幕旋转。安装弹窗默认是竖屏但如果设备之前被横屏应用占用弹窗可能以横屏形式出现。脚本开头用adb shell settings put system user_rotation 0锁定竖屏能避免这个问题。vivo 部分机型的安装弹窗有动画效果按钮是从底部滑上来的。如果截图截得太早按钮还没完全显示模板匹配会失败。我在轮询逻辑里加了一个 500 毫秒的初始延迟等动画结束再开始检测成功率明显提升。4.2 安装速度慢与 ADB 超时设置pm install在 vivo 手机上执行速度受安全扫描影响一个 100MB 的 APK 可能跑 30 秒以上。ADB 默认的超时时间不够用需要调整。在 AirTest 的配置里可以设adb_timeout或者直接在subprocess调用时加timeout参数。我实测的数据X100 上安装 50MB APK 平均 8 秒Y78 上 15 秒S18 上 12 秒。如果超过 30 秒还没动静大概率是卡在某个弹窗上了需要检查轮询逻辑是否正常。安装速度还和 USB 连接质量有关。用 USB 2.0 接口比 3.0 慢一倍用劣质数据线可能频繁断连。建议用原装线或者质量好的短线接口插在主板直出的 USB 口上不要用前面板扩展。提示如果设备数量多可以考虑用 USB Hub 扩展但要注意 Hub 的供电是否充足供电不足会导致设备频繁掉线。4.3 常见问题速查表问题现象可能原因解决方式pm install卡住不返回安装验证弹窗未处理检查轮询逻辑确认按钮模板匹配成功弹窗出现但点击无效坐标偏移或控件遮挡改用图像匹配降低阈值到 0.6检查屏幕旋转安装报错INSTALL_FAILED_INVALID_URIAPK 路径不对改用/data/local/tmp/路径安装报错INSTALL_FAILED_UPDATE_INCOMPATIBLE签名冲突先卸载旧版本或加-d参数安装报错INSTALL_FAILED_INSUFFICIENT_STORAGE存储不足清理设备空间或把 APK 推送到外部存储设备频繁掉线USB 供电或线材问题换线、换接口用带供电的 HubAirTest 连接设备失败ADB 版本不匹配统一 ADB 版本重启 ADB 服务文本匹配找不到按钮控件不是标准 Android 节点改用图像识别兜底4.4 实操心得与避坑建议踩过的坑里有几个特别值得说。第一个是关于 vivo 的“安装验证”开关。在设置里搜索“安装验证”会看到一个“安装应用时验证”的选项关掉它之后部分机型确实不再弹验证框了。但这个选项在不同系统版本里名字不一样有的叫“安全验证”有的叫“外部来源应用验证”。我在脚本里没有依赖这个设置而是直接用 ADB 命令关验证因为设置项的位置和名称变数太大ADB 命令更稳定。第二个是关于多设备并行。如果你同时跑 5 台以上的设备ADB 服务可能会因为并发连接数过多而变慢。解决办法是给每台设备分配独立的 ADB 端口或者用 AirTest 的多设备管理接口。我自己的做法是分批执行每批最多 4 台跑完一批再跑下一批牺牲一点速度换稳定性。第三个是关于截图模板的维护。vivo 系统更新后安装弹窗的样式可能会变之前截的模板就失效了。我建议在脚本里加一个“模板自检”功能每次运行前用一张预设的测试图跑一遍匹配匹配失败就报警提醒更新模板。这个机制帮我省了不少排查时间。第四个是关于日志记录。AirTest 的日志默认输出到控制台但多设备跑的时候控制台会乱。我会把每台设备的操作日志写到独立文件里用log()接口记录关键步骤出问题时直接看对应设备的日志文件比翻控制台快得多。最后分享一个提升稳定性的小技巧在pm install执行前先执行一次adb shell am force-stop com.android.packageinstaller把安装器界面重置一下。有时候安装器会残留上一次的状态导致新安装的弹窗不弹出或者弹出异常。强制停止一下相当于给它一个干净的环境实测能减少 30% 左右的异常情况。这套方案我在自己的设备农场上跑了小半年覆盖了 vivo 的几款主流机型日均安装量在 200 次左右成功率稳定在 95% 以上。剩下的 5% 失败案例大部分是设备硬件问题或者 USB 连接抖动换线换口就能解决。代码部分还在持续迭代比如最近在尝试用 OCR 替代图像模板减少维护成本等跑稳了再整理出来。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Modern JavaScript Tutorial 实战:范围外判断的两种等价写法——NOT 变体与德摩根定律 2026/10/1 9:57:18

Modern JavaScript Tutorial 实战:范围外判断的两种等价写法——NOT 变体与德摩根定律

文档/教程前端 【免费下载链接】en.javascript.info Modern JavaScript Tutorial 项目地址: https://gitcode.com/gh_mirrors/en/en.javascript.info 点击查看 免费下载 导读 本文围绕 en.javascript.info(Modern JavaScript Tutorial)中&…

阅读更多 →
DeepSeek Harness 插件实战:dshmarket 与 modlens 安装配置及故障排查指南 2026/10/1 9:57:11

DeepSeek Harness 插件实战:dshmarket 与 modlens 安装配置及故障排查指南

1. 为什么我要花时间折腾 DeepSeek Harness 插件第一次接触 DeepSeek Harness 是在一个做智能体工作流的朋友那里。他当时给我演示了一段自动化流程:从本地知识库拉取资料,经过模型推理,再自动生成结构化的项目文档,整个过程行云流…

阅读更多 →
多租户Odoo SaaS部署实战:Docker架构、实例管理与避坑指南 2026/10/1 9:57:11

多租户Odoo SaaS部署实战:Docker架构、实例管理与避坑指南

简介:整套基于Docker的多租户Odoo实例管理方案,适合具备服务器管理经验、负责企业级Odoo部署与运维的技术人员。该资源详细讲解SaaS Kit工具包的安装配置流程,包括Python依赖库的安装、目录结构搭建、Nginx与PostgreSQL配置、Odoo用户权限调整…

阅读更多 →
技术选型的经历,比「我用了什么」值钱得多 2026/10/1 9:57:05

技术选型的经历,比「我用了什么」值钱得多

技术简历上写「使用 Kafka 实现异步解耦」,和写「在 Kafka 和 RabbitMQ 之间选了前者,因为……」,是两个层级。 前者说明你会用,后者说明你会判断。而工作年限越长,后者的权重越高。 为什么选型经历值钱 因为它暴露的是…

阅读更多 →
OFDM低复杂度无边带SLM改进:分组选择映射与幅值标记法 2026/10/1 9:57:05

OFDM低复杂度无边带SLM改进:分组选择映射与幅值标记法

简介:面向无线通信与信号处理领域,这份资源针对OFDM系统峰均功率比(PAPR)过高的问题,提出基于选择映射(SLM)的低复杂度改进方案。传统SLM需多次IFFT计算候选信号,还要传输边带信息&a…

阅读更多 →
内存又炸了!我用C# IAsyncEnumerator给国产库做“流式Left Join“,2亿行数据OOM从此说拜拜 [特殊字符] 2026/10/1 9:57:05

内存又炸了!我用C# IAsyncEnumerator给国产库做“流式Left Join“,2亿行数据OOM从此说拜拜 [特殊字符]

🩸 一、 翻车剖析:传统 JOIN 的"内存黑洞"是怎么形成的? 很多新手老铁写 LEFT JOIN,脑子里想的是这样的: // ❌ 反面教材:内存黑洞写法 var orders await conn.QueryAsync(“SELECT * FROM t_or…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉