做移动端自动化这行如果有人问我第一个必须吃透的工具是什么我十有八九会回答adb。这玩意儿听起来像个命令行小工具但它几乎贯穿了自动化脚本的整个生命周期连接设备、模拟点击、安装应用、抓取日志、监控状态样样都离不开它。我刚入行时第一次在终端敲下adb shell input tap 540 960手机屏幕像被一只看不见的手戳了一下——那一刻我就意识到这不只是一个调试工具它是自动化脚本的万能钥匙。这篇文章我会从实际使用的角度出发把adb自动化脚本从环境搭建、命令拆解到多设备并发、常见坑位一次讲清楚适合刚接触自动化的测试新手也适合想用脚本解放双手的普通开发者。1. ADB自动化脚本的设计与整体思路1.1 ADB到底解决了什么问题先说个最直观的场景你手里有台安卓手机想自动完成“打开应用→点击某个按钮→滑动页面→截图”这一连串操作。如果全靠人肉点一次两次还行次数多了不仅浪费时间还容易点错。而adb给了我们一条从电脑直接指挥手机的通道通过它发出的命令可以控制手机的触控、按键、应用启动、数据读取等方方面面。换句话说adb就是PC和Android设备之间的“遥控器总线”。它不是某个App里的功能而是安卓系统自带的调试工具地位相当于Linux里的shell。自动化脚本想要“可编程地操作手机”底层几乎绕不开adb这也是为什么很多自动化框架在运行前都要先检查adb devices能不能正常识别设备。在实际项目中我用adb自动化脚本解决过不少实际问题每天定时清理手机后台进程和缓存文件模拟用户操作释放内存批量给几十台测试机安装同一款App省去手动一个个拖拽安装自动跑一轮App启动耗时测试连续冷启动20次记录时间无人值守时抓取崩溃日志把logcat输出保存成文件供后续分析用脚本模拟用户滑动、点击、截图做简单的冒烟测试。你会发现这些场景有一些共同点操作重复、量大、需要稳定执行、最好不动手。这正是adb自动化脚本最擅长的领域。1.2 为什么选ADB而不是其他方案很多刚接触自动化的朋友会纠结现在有Appium、Airtest、Auto.js这么多工具为什么还要专门学adb我的观点是adb是地基其他工具是盖在上面的房子。Appium这样的框架确实能精确定位控件而且跨平台但它的底层驱动Android的部分仍然是依赖adb去安装和启动应用再通过UiAutomator等机制做控件解析。Airtest确实能用图像识别来点击但它脱离不了adb来连接设备。Auto.js这类手机端脚本工具确实方便但它需要在手机上安装App并授予无障碍权限对部分设备和系统版本有兼容问题。如果你只是快速做一个脚本或者做一些框架覆盖不到的底层操作直接敲adb是最直接、最轻量的方式。比起动辄几百MB的自动化框架adb工具包本身只有几MB不需要在手机上装额外的服务端只要开了USB调试就能连几乎没有环境依赖。给个选型参考方案适用场景优点缺点adb原始命令连接管理、模拟点击、应用操作、日志抓取轻量、稳定、可用shell脚本直接控制无法直接获取控件层级依赖坐标或命令Appium跨端UI自动化、复杂断言支持控件定位、多语言编写环境重、启动慢、学习曲线陡Airtest游戏自动化、图像识别可视化脚本、适合游戏场景图像识别受分辨率影响较大Auto.js手机端脚本无需电脑、直接在手机运行无障碍权限限制、部分系统可能封禁所以我的建议是先把adb学扎实再根据项目需要去扩展框架。遇到框架失灵的时候回头看adb命令往往是排查问题的突破口。1.3 环境搭建从零到能跑通第一行命令这部分看着基础但很多坑都藏在这里。先说Windows下的配置方法Mac和Linux相对简单。第一步下载adb工具。一般不需要装完整版Android Studio直接下载platform-tools就可以里面包含了adb、fastboot等常用工具。解压后放到一个固定目录比如D:\platform-tools。第二步配置环境变量。右键“此电脑” → 属性 → 高级系统设置 → 环境变量在“系统变量”里找到Path把D:\platform-tools追加进去。配置好后重新开一个命令行窗口输入adb version如果输出版本信息就说明配置成功。第三步手机打开开发者选项。一般是在“关于手机”里连续点击“版本号”7次然后在设置里找到“开发者选项”打开“USB调试”。不同品牌的入口位置可能不太一样但基本都在设置里搜“开发者选项”就能找到。第四步连接手机。用数据线把手机连到电脑首次连接时手机会弹窗询问“是否允许USB调试”勾选“始终允许”并确认。然后在电脑终端输入adb devices看到类似下面这样的输出就说明连接成功List of devices attached R58M1234567 device如果显示unauthorized说明手机上没点授权弹窗重新插拔数据线或执行adb kill-server再adb start-server后重试即可。我还遇到过没有数据线的情况其实adb也支持无线连接。只要手机和电脑在同一局域网先用数据线连接一次执行adb tcpip 5555然后拔掉线再执行adb connect 192.168.x.x:5555就能走WiFi调试了。这种方式在做长时间自动化任务时特别实用不用一直挂着数据线。2. 核心命令与脚本化的关键细节2.1 连接管理一切脚本的前提自动化脚本第一步就是确保设备在线所以连接管理命令必须滚瓜烂熟。adb devices是最常用的它列出当前连接的所有设备状态。常见状态有三种device表示正常连接unauthorized表示未授权offline表示设备离线。写脚本时我习惯先做一次连接检查设备不在线就直接退出避免后面命令全部报错。adb -s 序列号 shell可以指定某台设备执行命令这在多设备场景下至关重要。比如你有两台手机不指定序列号时adb会报错说“more than one device”只有用-s指定目标设备才能精确操作。日常维护中还可能需要重启adb服务。设备长时间连接后偶尔会变得迟钝执行adb kill-server再adb start-server往往能解决。注意这两个命令会中断当前所有adb连接别在自动化跑到一半的时候手痒乱敲。2.2 高频操作命令盘点我这里整理了一份脚本中经常用到的adb命令清单每条都标注了用途方便你随时查阅。adb shell pm list packages列出已安装应用包名经常用于检查目标应用是否安装。adb shell pm list packages -3只显示第三方安装的应用区分系统应用很方便。adb shell am start -n 包名/Activity名启动指定应用的指定界面。获取Activity名可以先执行adb shell dumpsys window | grep mCurrentFocus从当前前台窗口看出来。adb shell am force-stop 包名强制停止应用相当于手动划掉后台任务。adb shell input tap x y模拟点击屏幕上坐标点。adb shell input swipe x1 y1 x2 y2 时长ms模拟从起点滑动到终点。adb shell input text 内容向输入框填入文本注意不能直接输入中文。adb shell screencap -p /sdcard/screen.png截图并保存到设备再配合adb pull拉到电脑。adb shell screenrecord /sdcard/demo.mp4录屏用于记录自动化执行过程。adb logcat -c清空日志缓冲区adb logcat log.txt把日志输出到本地文件。adb shell dumpsys meminfo 包名查看应用内存占用。adb shell dumpsys batterystats --enable full-wake-history开启电池和唤醒历史的详细统计之后可以导出分析设备耗电情况。adb install -r apk路径覆盖安装应用相当于保留数据的重新安装。这些命令看着零散但组合起来能实现非常强大的自动化流程。比如我要做一个“自动启动应用并截图”的脚本只需要三行命令am start启动screencap截图pull拉取。逻辑简单可靠。2.3 坐标体系为什么你的点击总是不准很多人在写模拟点击脚本时都会遇到一个问题为什么我明明算好了坐标脚本点下去却不对这就要理解Android的坐标体系。adb shell input tap使用的是屏幕的绝对像素坐标原点在屏幕左上角x轴向右y轴向下。不同设备的分辨率和DPI不同同样一个逻辑位置在1080x1920和1440x3200上的像素坐标完全不一样。所以我建议在写坐标点击脚本前先执行adb shell wm size看看分辨率执行adb shell wm density看DPI。如果脚本要适配多种设备最好把坐标都写成一个相对比例而不是写死绝对像素。比如把点击位置表达成“屏幕宽度的50%屏幕高度的30%”然后在代码里根据实际分辨率动态计算。另外还要注意横竖屏切换对坐标的影响。横屏时坐标轴也会旋转我踩过一次坑在竖屏下算好的坐标切到横屏后点到了完全无关的区域。解决方案是脚本执行前先判断当前屏幕方向或者强制应用保持竖屏。3. 实操记录从单条命令到完整自动化脚本3.1 第一个脚本定时清理后台应用直接写命令叫“用adb”但真正能被称作“自动化脚本”的是把它写成可在任意时间重复执行的一组逻辑。我先分享一个我自己常用的清理脚本逻辑很简单启动脚本后先检查设备连接状态然后依次强制停止指定的应用包名。假设我们要清理微信、支付宝和某个游戏应用可以写一个shell脚本Linux/macOS或Windows的Git Bash环境都能跑#!/bin/bash adb devices | grep -w device /dev/null if [ $? -ne 0 ]; then echo 设备未连接请检查adb devices输出 exit 1 fi packages( com.tencent.mm com.eg.android.AlipayGphone com.tencent.tmgp.somegame ) for pkg in ${packages[]}; do echo 正在清理 $pkg adb shell am force-stop $pkg done这段脚本做的事情很朴素但你把它配合系统的计划任务Windows的任务计划程序或crontab使用就能实现每天凌晨自动清理后台应用省去了手动划任务的麻烦。实际工作中我还会在清理前先执行adb shell am kill-all来杀掉所有后台进程再针对个别顽固应用做force-stop。注意一点force-stop对系统应用需要一定权限部分厂商ROM会限制。遇到这种情况退而求其次用am kill只能终止允许被杀死的后台进程效果不如force-stop彻底但至少不会误伤系统关键进程。3.2 模拟用户操作自动滑动与点击比清理后台更常见的需求是模拟真实用户操作。比如你想让手机自动刷一会儿短视频或者自动走一遍App的新手引导核心就是input swipe和input tap。这里分享一个真实案例。之前我负责的App有一个签到功能入口在首页顶部需要先点击签到按钮再在弹出的弹窗里点击“立即签到”最后还要截图保存结果。我把这个流程写成了Python脚本用subprocess调adb命令虽然简单但很能说明问题import subprocess import time def adb_shell(command): subprocess.run(fadb shell {command}, shellTrue, checkTrue) # 1. 启动应用 adb_shell(am start -n com.example.app/.MainActivity) time.sleep(3) # 2. 点击首页签到按钮坐标按1080x1920分辨率的相对位置计算 adb_shell(input tap 540 300) time.sleep(2) # 3. 点击弹窗确认按钮 adb_shell(input tap 540 960) time.sleep(1) # 4. 截图并拉到本地 adb_shell(screencap -p /sdcard/checkin.png) subprocess.run(adb pull /sdcard/checkin.png ./, shellTrue) print(签到完成)这段代码没有任何高深技术但它是理解adb自动化的良好起点启动、点击、等待、截图这就是脚本最基本的闭环。需要注意sleep的时间要合理太短会导致界面还没加载出来就点击太长又影响效率。我一般会根据页面加载速度动态调整必要时通过dumpsys window判断当前窗口是否已经切换。实际测试还有个容易忽略的点如果目标按钮在刷列表时才会出现直接tap会找不到位置。这种情况下我的做法是先执行几条swipe命令把列表滚动到目标区域再执行点击。这种“先滑动后点击”的模式在自动化脚本里非常常见。3.3 批量安装几十台设备的提效利器日常测试经常要往多台手机装同一个App手动一个个拖APK简直折磨。用adb脚本批量安装效率完全不同。我先讲核心命令adb install -r path/to/app.apk-r表示覆盖安装保留数据。如果APK的targetSdk版本比较新而设备Android版本较旧可能会碰到安装失败提示INSTALL_FAILED_DEPRECATED_SDK_INT之类的问题。此时可以加一个参数adb install --bypass-low-target-sdk-block -r app.apk绕过targetSdk校验。这个参数是Android 14之后引入的但用来解决SDK兼容问题很实用。批量安装的脚本逻辑大致如下#!/bin/bash adb devices | grep -w device | awk {print $1} devices.txt while read -r device; do echo 开始安装到 $device adb -s $device install -r ./app.apk if [ $? -eq 0 ]; then echo $device 安装成功 else echo $device 安装失败 install_fail.log fi done devices.txt这个脚本先把设备列表读出来然后逐台安装失败的信息写入日志。虽然结构简单但比手动操作强太多了。如果在Windows自带的cmd里跑语法会有差异我更推荐装一个Git Bash或者直接用Python写循环。3.4 日志与状态采集让脚本更智能真正让adb自动化脚本显得“智能”的地方在于它会读取设备状态并做出判断。你可以用dumpsys系列命令拿到系统内部数据然后让脚本根据这些数据决定下一步行动。举个例子之前做功耗测试时需要分析App在后台的唤醒情况。我先执行adb shell dumpsys batterystats --enable full-wake-history让设备开始记录详细的唤醒历史。测试跑一段时间后再执行adb shell dumpsys batterystats batterystats.txt把这个文件导出来就能看到App的wakelock唤醒锁持有时间、CPU运行时间、网络活动等统计数据。脚本可以根据这些数据分析出App是否存在异常频繁唤醒导致耗电过快的问题。再比如自动化过程中如果发现页面没有跳转成功可以在脚本里加入adb shell dumpsys window | grep mCurrentFocus获取当前前台窗口的Activity和期望的Activity做对比。不一致就重试重试N次还不行就打日志并退钉。这种“判断—重试—退出”的逻辑是所有可靠自动化脚本的共同特性。4. 进阶玩法多设备并发与UI控件交互4.1 多设备并发控制的几个思路很多人用adb连多台设备时都会遇到同一个问题不加-s参数adb命令根本没法执行因为设备不唯一。我处理多设备自动化主要有三种模式。第一种最简单就是前面批量安装脚本里展示的逐台循环。它虽然挨个执行但胜在稳定脚本好写也不会把设备搞乱。缺点是当设备数量多、操作耗时长时整体时间线性增长。第二种是并行执行利用shell的操作符或Python的ThreadPoolExecutor。比如需要在10台设备上同时启动某个App可以给每台设备开一个子进程执行命令from concurrent.futures import ThreadPoolExecutor devices [device_a, device_b, device_c] def start_app(device): cmd fadb -s {device} shell am start -n com.example/.MainActivity subprocess.run(cmd, shellTrue) with ThreadPoolExecutor(max_workers5) as executor: executor.map(start_app, devices)注意max_workers不要开太多否则电脑CPU和USB带宽都会吃紧反而容易出问题。我一般控制在3到5个并发。第三种是使用某些自动化测试平台或框架内置的多设备管理能力。但即便用了框架底层最终还是落到adb的-s参数上理解这个本质能帮你快速排查各种并发异常。4.2 从坐标到控件文本用adb读取UiAutomator属性前面讲的点击都是基于坐标但真实项目的UI元素位置经常变动坐标一旦失效脚本就废了。这时候需要一种能拿到控件信息的方式。adb本身提供了一个很好用的命令adb shell uiautomator dump /sdcard/ui.xml adb pull /sdcard/ui.xml它会把当前界面所有控件的层级和属性导出成XML文件。你可以在这份XML里搜索控件文本、resource-id或content-desc然后计算出该控件的bounds中心点坐标再用input tap去点击。这样做的好处是脚本不再依赖写死的坐标而是根据控件实际位置动态定位。比如搜索一个文本为“签到”的Button然后提取它的bounds属性[400,900][680,1020]可以算出中心点是(540, 960)再交给点击命令执行。这套思路虽然不如Appium那样优雅但在轻量脚本里完全够用。而且一旦掌握你还能用它解决很多奇怪的UI自动识别问题比如不同分辨率适配。4.3 权限增强思路Shizuku与无USB调试场景有些自动化脚本想执行的高权限操作在非root设备上会被限制。比如直接控制系统设置、模拟其他应用点击等。除了root还有一个折中方案是借助Shizuku。Shizuku的运行原理是通过adb授予一个系统级API权限给手机上的一个小服务之后这个服务可以代表你执行很多高权限命令而不需要重新插USB线。它启动后即使和电脑断开连接手机上的脚本依然可以调用shizuku shell来执行命令。举个例子如果你通过Shizuku的API运行sh /storage/emulated/0/android/data/com.omarea.vtools/up.sh这其实是借助Shizuku权限去执行一个脚本文件完成一些普通App做不到的系统级修改。使用Shizuku时需要先在电脑上执行一次adb shell sh /sdcard/Android/data/moe.shizuku.privileged.api/start.sh之类的启动脚本之后手机会弹窗请求授权确认后就生效了。不过要提醒一句这类高权限方案有成百上千种应用场景但也要注意隐私和合规。脚本里不要做任何越权获取用户信息、绕过应用检测的事情仅用在工作所需的正规自动化测试中。4.4 模拟器与电视设备的特殊处理说到设备多样性模拟器和智能电视也经常出现在adb自动化需求里。模拟器如夜神、雷电、MuMu本质上是一个运行在电脑上的Android虚拟机它默认会开一个adb端口供外部连接。打开模拟器后你直接执行adb connect 127.0.0.1:5555或adb connect 127.0.0.1:62001不同模拟器端口不同就可以连上。连接成功后模拟器就被当成一台普通设备来操作。用adb命令控制游戏自动刷初始号、跑图之类的场景就是这样实现的。电视盒子、智能电视也支持adb很多老款智能电视在设置里没有直接的开发者选项需要连续按“版本号”之类的隐藏入口个别品牌甚至要输入特定校验码才能开启。打开adb后电视和电脑连同一个局域网就可以adb connect 电视IP:5555连接。连上之后用input keyevent KEYCODE_DPAD_UP、KEYCODE_DPAD_DOWN这类按键事件来控制焦点移动代替物理遥控器完成操作。这在做电视端App自动化测试时非常实用。5. 常见问题与排查技巧实录5.1 高频连接异常速查表总有人问我“为什么我adb连不上设备”其实大部分问题都集中在下面这几种情况。我整理了一张速查表可以对照着排查。异常现象常见原因快速解决办法adb devices显示 unauthorized手机未点“允许USB调试”授权弹窗拔插数据线重新弹窗并勾选始终允许或执行adb kill-server后重连设备显示 offlineadb服务版本与设备端不匹配或服务异常执行adb kill-server和adb start-server重启服务换一根数据线error: could not initialize winsock: a system call has failed. (10107)Windows下winsock初始化异常常见于杀毒软件/网络驱动干扰重启adb服务以管理员身份运行命令行关闭或暂时卸载可疑安全软件后重试more than one device连接了多台设备但命令未指定序列号在命令里加-s 序列号参数找不到设备没开启USB调试、驱动没装好、线缆只支持充电不支持数据传输确认开发者选项安装设备驱动换双通数据线adb不是内部或外部命令环境变量没配置或PATH未生效重新配置platform-tools路径新开命令行窗口INSTALL_FAILED_TARGET_SDK_TOO_LOW设备系统版本比APK targetSdk低使用adb install --bypass-low-target-sdk-block参数安装连接WiFi后不稳定手机息屏会断开WiFi或限制adb后台在开发者选项里开启“保持唤醒”或使用usb连接5.2 逐步排查连接正常但点击没反应这是很诡异的一种故障adb devices正常adb shell也能执行但input tap或input swipe就是没反应。我遇到过几次这种情况排查路径基本上是这样的先确认当前屏幕是不是解锁状态。手机锁屏时input tap点击会失效因为点击事件被锁屏拦截了。先执行adb shell input keyevent KEYCODE_WAKEUP唤醒屏幕再用adb shell wm dismiss-keyguard解锁。再检查有没有App悬浮窗遮挡。某些应用会把触摸事件拦截在自己的窗口层导致点击命令被吃掉。这种情况可以用adb shell dumpsys window | grep -i input查看输入分发目标如果目标窗口不是你想点的那个大概率是有系统弹窗或悬浮按钮盖住了。最后检查坐标是否正确。特别是横竖屏切换后坐标轴会变化之前竖屏的坐标在横屏下点击的位置完全不一样。我碰到过最坑的一次是某款手机系统自带“单手模式”把屏幕内容缩放了30%所有坐标自然全偏了。关闭单手模式后恢复正常。5.3 脚本稳定性的几个建议写adb自动化脚本最容易翻车的地方不是命令不会写而是脚本不够健壮。我在实践中总结了几条经验第一脚本每一步操作之间一定要加合理延迟。不要指望命令一条条飞快执行完App的页面加载、动画过渡都需要时间。sleep 1可能不够3秒也可能长了建议根据场景实测调整必要时在点击前做页面元素判断。第二操作前先获取当前窗口焦点确认页面到位后再执行后面的动作。我常用adb shell dumpsys window | grep mCurrentFocus做一个前置校验。匹配不到期望的Activity时不继续执行先截图留证再退出脚本。这个设计帮我避免了很多“点半天结果全点在错误页面”的情况。第三脚本要有日志。每做一步就把执行结果写入一个日志文件出问题时能直接定位到哪一步失败。简单做法是在shell里用 log.txt追加输出Python里更简单直接用logging模块。第四尽量不在脚本里写死复杂坐标。能用uiautomator dump动态解析控件位置就动态解析不能的话至少把坐标写成相对比例。这样换设备、改分辨率时只需要改一行配置而不是整个脚本。还有一个小技巧长时间跑自动化前先在设备设置里关闭“屏幕超时自动锁屏”或者发送一个adb shell settings put system screen_off_timeout 1800000把熄屏时间改成30分钟否则脚本跑到一半手机锁屏后面的操作基本全废。5.4 避坑实录那些折腾到深夜的问题最后分享几个让我印象深刻的“坑”也都是网上搜索热度很高的问题。第一个是winsock初始化报错。有一回在Windows上跑自动化突然所有adb命令都报error: adb: could not initialize winsock: a system call has failed. (10107)。这个报错看着像是adb本身出了问题其实是系统winsock环境被某些软件改动或干扰了。我最后是用管理员权限打开命令行执行adb kill-server和adb start-server把adb拿到管理员权限下重新初始化才解决。如果还不行可以试试在网络适配器设置里重置一下网络栈。第二个是电视或盒子上的adb二维码问题。现在一些新款电视或机顶盒开启adb后不再显示IP端口而是显示一个二维码需要用专门的计算工具去解码获取加密的adb连接信息。这种机制本质上是厂商为了安全做的限制。如果你遇到这种情况只能去查对应型号的开启教程或者借用同型号设备上已验证的校验码计算器没有特别通用的办法。第三个是真机连接时提示“手机无响应”。多数时候不是adb的问题而是手机弹了一个授权对话框或者“是否允许USB调试”之类的系统弹窗挡住了后续操作。脚本里遇到这种异常建议先把设备状态打出来人工确认屏幕上有没有弹窗再决定是否继续。还有一个小众但很实用的问题有时候adb install提示低版本SDK限制用--bypass-low-target-sdk-block参数可以强制安装。这个参数在自动化安装老版本APK到新系统上时相当有用但注意它只是绕过了安装时的SDK版本检查不保证安装后运行正常所以装完最好再自动启动一次应用验证。我个人在实际操作中最大的体会是adb自动化脚本的价值不在于脚本本身有多花哨而在于它能稳定、可重复地完成那些“手动操作完全可以做但不想反复做”的事情。你越是把基础命令吃透越是学会在脚本里加入状态判断和错误处理你的自动化方案就越可靠。如果这篇文章让你少踩几个我踩过的坑那就值了。最后再分享一个小技巧给adb命令单独做一个shell函数或Python封装统一处理设备选择、日志输出和异常退出长期来看能省下非常多重复劳动。
