1. 项目概述与整体方案
1.1 先说清楚Shizuku和ADB是什么关系
很多人听到Shizuku第一反应是"又一个Xposed框架",其实完全不是一回事。Shizuku本身不是一个Hook框架,它是一个运行在Android系统上的服务进程,帮你把ADB(Android Debug Bridge)的权限能力长期"借用"给普通App使用。换句话说,Shizuku的本质是:把ADB shell的权限通过系统服务的方式暴露给应用层,让普通App能直接调用高权限API。
ADB大家应该不陌生,它是Android官方的调试桥,电脑通过USB或无线网络连接手机后,能用命令行做很多事情:安装应用、抓日志、模拟点击、查看窗口层级、截图、改系统设置等。但它有个痛点——只能"临时"用,电脑拔线就断。而Shizuku正好补上了这个缺口:它把ADB的命令权限固定在系统进程里,之后即使不用电脑,App也能通过Shizuku调用这些能力。
把这两者组合起来做自动化脚本,本质上是解决一个问题:在没有root权限的情况下,如何稳定、长期地让手机替我们执行重复操作。无论是批量测试App、自动签到、爬取数据、或是做无障碍辅助,这条路线都比root方案更安全、也更通用。
1.2 这套方案能做什么
做了快三年自动化相关工作,我常用这套方案解决以下几类需求:
| 场景 | 具体操作 | 传统方式的问题 | Shizuku+ADB方案 |
|---|---|---|---|
| UI自动化测试 | 启动App、模拟点击、截图比对 | 需要root或MonkeyRunner | 通过uiautomator dump获取控件树,精确点击 |
| 系统状态监控 | 抓取CPU频率、电池温度、网络状态 | 需要root权限 | 直接执行adb shell命令拿到实时数据 |
| 批量任务 | 批量安装/卸载应用、批量改设置 | 逐个操作太慢 | 脚本循环执行adb命令,一台电脑控制多台设备 |
| 无障碍辅助 | 自动填写表单、自动切换应用 | 需要开启无障碍服务 | 用adb shell命令绕过部分限制 |
我个人的看法是:这套方案最大价值不在"能不能做",而在于普通开发者也能做。不需要编译内核、不需要解bl锁,只要手机能开USB调试,就能搭起一套自动化能力。
2. 环境准备:Shizuku安装与ADB工具链配置
2.1 Shizuku安装的三种激活方式
Shizuku的安装其实很简单,GitHub Releases页面下载最新APK装上就行,但激活方式有三条路,需要根据自己手机情况选择。
方式一:无线调试激活(Android 11及以上)
这一种是我目前最推荐的。Android 11开始系统内置了无线调试功能,不需要电脑就能激活Shizuku。具体步骤如下:
- 手机开启开发者选项,进入"无线调试"并打开
- 点击"使用配对码配对设备",会弹出一个6位配对码
- 用另一台设备(或同一台手机的终端)执行
adb pair 手机IP:端口 配对码完成配对 - 配对后在Shizuku App里点击"通过无线调试启动"即可
这里有个细节:配对用的端口和连接用的端口不一样。配对成功后,无线调试界面会显示一个新的IP和端口,这个才是后续连接用的。
方式二:ADB线连激活(所有Android版本)
传统方式,用USB线连接电脑:
adb devices adb shell sh /sdcard/Android/data/moe.shizuku.privileged.api/start.sh这条命令的本质是启动Shizuku服务进程。服务启动后,Shizuku App里会显示"正在运行"。
方式三:root设备直接激活
有root的手机最简单,在Shizuku里直接点"通过root启动",选择授予root权限即可。这种方式的好处是重启后Shizuku会自动恢复,不需要再手动激活。
2.2 ADB工具链的下载与环境变量配置
ADB工具通常从Android开发者官网下载platform-tools压缩包,Windows用户要注意版本,64位系统就用64位版本。下载后解压到一个固定目录,比如D:\platform-tools。
Windows环境变量配置:
打开"系统属性 - 高级系统设置 - 环境变量",在"Path"中加入D:\platform-tools,然后打开新的CMD窗口输入adb version验证。如果提示"adb不是内部或外部命令",说明路径没配对。
关于ADB版本不匹配的问题,热词里提到adb server version (31) doesn't match this client (41),这个我后面会有专门章节讲排查方法。这里先说结论:电脑端和手机端的ADB协议版本必须一致,老旧手机的ADB协议版本可能落后,需要手动更新platform-tools或者用兼容模式。
2.3 手机端调试模式的正确打开方式
开发者选项的开启方法各品牌不太一样,但核心路径一致:设置里连续点击"版本号"7次。需要注意:
- 小米/红米手机除了开启USB调试,还要开启"USB调试(安全设置)",否则无法通过ADB模拟点击
- OPPO手机在开发者选项里要关闭"权限监控"
- vivo手机要在"USB调试"下再开"USB调试(安全权限)"
- 华为/荣耀需要登录华为账号才能开启ADB调试
这些细节不处理好,后面跑自动化脚本会各种报错,比如明明adb devices能看到设备,但adb shell input tap就是没反应。
3. 核心实操:ADB命令与Shizuku的配合使用
3.1 最常用的ADB命令清单
做自动化脚本,有几类命令是高频使用的。我这里整理了一份实用清单,几乎每个自动化项目都会用到。
设备连接与状态查询:
adb devices # 列出当前连接的设备 adb get-state # 获取设备状态(device/offline/unauthorized) adb shell getprop ro.product.model # 获取设备型号 adb shell getprop ro.build.version.release # 获取Android版本模拟操作:
adb shell input tap x y # 模拟点击坐标点 adb shell input swipe x1 y1 x2 y2 duration # 模拟滑动手势 adb shell input text "hello" # 输入文字(不支持中文) adb shell input keyevent 4 # 模拟按键,4代表返回键 adb shell input keyevent 3 # 模拟Home键窗口与UI层级:
adb shell uiautomator dump /sdcard/window_dump.xml # 导出当前窗口控件树 adb shell dumpsys window # 查看窗口焦点信息 adb shell wm size # 查看屏幕分辨率 adb shell wm density # 查看屏幕密度系统设置修改:
adb shell settings put system screen_brightness 200 # 设置屏幕亮度 adb shell settings put global stay_on_while_plugged_in 3 # 充电时常亮 adb shell wm set-user-rotation lock 1 # 锁定屏幕方向为横屏 adb shell locksettings set-disabled true # 关闭锁屏密码(仅在测试设备上用)截图与录屏:
adb exec-out screencap -p > screen.png # 截图保存到电脑 adb exec-out screenrecord --time-limit 10 - > video.mp4 # 录屏10秒保存到电脑这些命令看着简单,但配合起来就是完整的自动化操作链。比如热词里提到"adb截图保存电脑",实际脚本中通常不是单独截图,而是"点击某个按钮 -> 等待加载 -> 截图 -> 保存 -> 下一步操作"这样的流程。
3.2 通过Shizuku授权应用执行Shell命令
这里要区分一个概念:直接用ADB执行命令,和通过Shizuku让App执行命令,场景完全不同。
如果只是在电脑上操作,直接adb就行,但我们做自动化脚本往往需要手机上运行App,由App来执行这些高权限命令。这时候Shizuku就派上用场了。
Shizuku提供了一套API,App可以通过它调用Runtime.exec()执行shell命令。具体代码逻辑(以Android Java为例):
// 检查Shizuku是否就绪 if (!Shizuku.ping()) { // 引导用户去启动Shizuku Shizuku.requestPermission(ACTION_REQUEST_PERMISSION); return; } // 执行shell命令 private static String execShellCommand(String cmd) throws IOException { ParcelFileDescriptor pfd = Shizuku.newProcess( new String[]{"sh", "-c", cmd}, null, null ); // 读取输出流 BufferedReader reader = new BufferedReader( new InputStreamReader(new FileInputStream(pfd.getFileDescriptor())) ); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); } return sb.toString(); } // 示例:获取当前前台应用的包名 String foregroundApp = execShellCommand("dumpsys activity activities | grep mResumedActivity");用这种方式,App就拥有了ADB shell级别的权限,但不需要手机root,也不需要在电脑上操作。实际项目中,很多自动签到App、自动打卡工具就是这么实现的。
3.3 用uiautomator dump实现精准点击
坐标点击太脆弱,屏幕分辨率一变脚本就报废。自动化脚本里真正的核心是根据控件ID或文本定位目标,而不是固定坐标。
uiautomator dump命令会把当前界面的控件树导出到XML文件,里面包含了每个控件的包名、类名、文本内容、资源ID、坐标bounds等信息。解析这个XML,就能精准定位并点击。
实际操作流程:
# 第一步:导出控件树 adb shell uiautomator dump /sdcard/window_dump.xml # 第二步:将XML拉到电脑 adb pull /sdcard/window_dump.xml . # 第三步:Python解析XML并执行点击Python侧的解析逻辑可以参考这个简版示例:
import xml.etree.ElementTree as ET import subprocess import re def parse_and_click(target_text): """解析控件树,根据文本内容查找并点击""" tree = ET.parse('window_dump.xml') root = tree.getroot() for node in root.iter('node'): if node.get('text') == target_text: bounds = node.get('bounds') # 解析bounds格式:[x1,y1][x2,y2] coords = re.findall(r'\[(\d+),(\d+)\]', bounds) x1, y1 = map(int, coords[0]) x2, y2 = map(int, coords[1]) center_x = (x1 + x2) // 2 center_y = (y1 + y2) // 2 # 执行点击 subprocess.run(['adb', 'shell', 'input', 'tap', str(center_x), str(center_y)]) print(f'点击成功: {target_text} at ({center_x}, {center_y})') return True print(f'未找到目标: {target_text}') return False # 使用示例 parse_and_click('确定')这里有个坑:uiautomator dump在导出过程中会请求窗口权限,偶尔会导致当前App失焦。解决办法是在dump前加一个小延时,或者用adb shell uiautomator dump --compressed减少数据量。另外,某些App做了防自动化检测,控件树可能是加密的或者根本没有text属性,这时候只能退回到坐标方案。
注意:uiautomator dump不支持在锁屏状态下执行,如果做了
locksettings set-disabled true关闭锁屏,截图和dump都会顺畅很多。
3.4 无线调试:摆脱数据线束缚
自动化脚本要真正方便,必须用无线连接。无线调试有两种模式,区分清楚能少踩很多坑。
模式一:Android 11内置无线调试
这是目前最推荐的,不依赖电脑,直接在手机端设置里开启"无线调试",然后用Shizuku的"无线调试启动"按钮就行。但要注意一个问题:无线调试默认只在Wi-Fi环境下生效,且部分品牌的手机在锁屏一段时间后会断开无线调试,需要到设置里关闭"休眠时断开连接"之类的省电选项。
模式二:传统ADB over Wi-Fi
Android 10及以下版本,手机连接到同一局域网后,用以下命令开启无线ADB:
adb tcpip 5555 adb connect 192.168.1.100:5555执行adb tcpip后,手机会重启ADB服务,然后就可以拔线了。这里有个容易踩的坑:重启手机后,无线ADB会回到关闭状态,需要重新用USB线执行一次adb tcpip。所以如果是频繁重启的设备,建议用Shizuku的方式,它能在每次开机后自动打开无线调试。
4. 实战案例:用Shizuku+ADB写一个自动化测试脚本
4.1 场景设定:自动遍历App核心页面
为了把前面的知识点串联起来,我设计一个实际案例:对一个测试App进行自动化遍历,自动进入每个Tab页面,截图保存到电脑,同时记录页面加载时间和是否有报错日志。
这个场景在企业App回归测试中特别常见,新版本发布前,手动点一遍几十个页面太耗时,用脚本十几分钟就能搞定。
4.2 脚本完整实现与参数解析
整个脚本分为三步:连接设备、执行遍历、处理结果。我用Python写了一个完整的可运行版本(此处为演示核心逻辑):
#!/usr/bin/env python3 import subprocess import time import os from datetime import datetime DEVICE_IP = "192.168.1.100" DEVICE_PORT = "5555" APP_PACKAGE = "com.example.demoapp" OUTPUT_DIR = "screenshots_" + datetime.now().strftime("%Y%m%d_%H%M%S") # 步骤一:连接设备 def connect_device(): # 先把设备添加到adb信任列表 subprocess.run(["adb", "connect", f"{DEVICE_IP}:{DEVICE_PORT}"]) # 等待连接稳定 time.sleep(2) result = subprocess.run(["adb", "devices"], capture_output=True, text=True) print(result.stdout) if "device" not in result.stdout: raise Exception("设备连接失败,请检查IP和端口") # 步骤二:启动App并等待加载 def launch_app(): subprocess.run(["adb", "shell", "monkey", "-p", APP_PACKAGE, "-c", "android.intent.category.LAUNCHER", "1"]) time.sleep(5) # 等待应用启动 # 步骤三:遍历并截图 def traversal_pages(): if not os.path.exists(OUTPUT_DIR): os.makedirs(OUTPUT_DIR) # 定义需要遍历的页面坐标(根据实际App调整) tab_coords = [ (100, 1800, "首页"), (300, 1800, "分类"), (500, 1800, "发现"), (700, 1800, "消息"), (900, 1800, "我的"), ] for x, y, page_name in tab_coords: # 点击底部Tab subprocess.run(["adb", "shell", "input", "tap", str(x), str(y)]) time.sleep(3) # 等待页面完全加载 # 截图并保存到电脑 screenshot_path = os.path.join(OUTPUT_DIR, f"{page_name}.png") with open(screenshot_path, "wb") as f: result = subprocess.run( ["adb", "exec-out", "screencap", "-p"], capture_output=True ) f.write(result.stdout) print(f"已保存: {screenshot_path}") # 抓取日志,检查是否有异常 logcat_output = subprocess.run( ["adb", "logcat", "-d", "-s", "AndroidRuntime:E"], capture_output=True, text=True ).stdout if "FATAL EXCEPTION" in logcat_output: print(f"警告: {page_name} 页面出现崩溃异常") # 关闭App subprocess.run(["adb", "shell", "am", "force-stop", APP_PACKAGE]) if __name__ == "__main__": connect_device() launch_app() traversal_pages() print("自动化遍历完成")这个脚本有几个关键参数需要着重说明:
延时参数的选择:点击Tab后为什么要等3秒?因为页面加载速度跟设备性能和网络环境有关。太短会截到空白页;太长浪费时间。我一般会先跑一次手动计时,取平均加载时间加1秒余量。也可以在循环里做动态判断,比如循环检测某个控件的出现,但这会增加脚本复杂度。
截图用exec-out而不是shell:adb shell screencap -p > file.png在Windows上会把文件结尾的\r\n错误地转成\n,导致照片格式损坏。用adb exec-out可以绕过这个问题,二进制数据原样输出。这个坑坑过我一次,特此说明。
日志筛选:-s AndroidRuntime:E表示只看AndroidRuntime标签的错误日志。这样能快速定位崩溃异常,不会被大量无用的系统日志淹没。
配合Shizuku使用时,可以把这些步骤从"电脑+ADB"换成"手机App+Shizuku"。具体做法是在App里把这几个Python步骤对应的shell命令封装起来,然后通过Shizuku API执行,效果几乎一致,但完全不需要电脑介入。
4.3 从脚本到平台的进阶:多设备并行
单台设备的自动化只是基础,实际测试场景往往是一台电脑控制多台手机同时跑脚本。这时代理连接和管理策略就很重要了。
ADB支持多设备连接,但命令需要用-s参数指定具体设备:
adb devices # 查看所有设备 adb -s 192.168.1.100:5555 shell input tap 100 100 adb -s SERIAL_NUMBER shell screencap -p > device1.pngPython脚本里可以用线程或进程池做并发控制,但要注意:多个设备同时执行uiautomator dump时,如果设备性能一般,偶尔会有冲突。稳妥的做法是加个全局锁,让每个设备的dump操作串行执行。
关于多设备并行,我的经验是先用2到3台设备跑通流程,确认脚本稳定后再扩展到更多设备。一次性上20台设备,如果脚本里有某个隐藏bug,崩溃起来排查会非常痛苦。
4.4 开机自启与实际部署的完整闭环
脚本写出来不算完,关键是要能稳定运行。我常用的部署组合是:
- **Tasker(或Automate)**在手机上定时触发脚本
- Termux作为shell脚本的运行环境
- 通过Shizuku授予Termux执行高权限命令的能力
这样手机上就形成了一个闭环:早上8点自动触发自动化任务,数据通过HTTP请求或邮件发送到指定服务器,全程无人值守。
热词里提到Termux安装ADB驱动并给其他手机刷机,其实Termux对这个素材的用法原理上是一致的——Termux提供了Linux环境,配合Shizuku就能在不root的情况下做很多本来需要root才能做的事。
5. 常见问题排查与避坑指南
5.1 高频问题速查表
做ADB自动化,有一些问题几乎每个人都会遇到。我把它们整理成了速查表,按出现频率排序。
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
adb unauthorized | 手机USB调试授权弹窗没点"允许" | 拔线重新连接,或执行adb kill-server后重新adb devices |
adb server version (31) doesn't match this client (41) | 电脑端和手机端ADB版本不一致 | 更新电脑端platform-tools,或卸载手机端第三方的ADB工具 |
daemon not running... could not read ok from adb server | 端口5037被占用 | 杀进程释放端口:netstat -ano找PID,taskkill /F /PID xxx |
| 无线调试连不上 | 防火墙拦截端口5555 | 检查电脑和手机是否在同一网段,关闭防火墙测试 |
| uiautomator dump导出的XML是空的 | 当前页面需要滚动,或App做了防自动化 | 先input swipe强制刷新布局,检查是否锁屏状态 |
| 截图显示全黑 | 屏幕已熄灭,或App在后台 | 先执行input keyevent 224(唤醒屏幕),等待1秒再截图 |
| 模拟点击无效 | 部分品牌手机USB调试权限受限 | 小米/OPPO/vivo需额外开启"USB调试安全权限" |
| 脚本执行到一半设备掉线 | USB线接触不良或休眠断网 | 换高质量数据线;手机设置里关闭Wi-Fi休眠;用Shizuku保持连接 |
5.2 深挖两个疑难问题
adb unauthorized反复出现
这个问题的本质是RSA密钥指纹不匹配。首次连接时,电脑会生成一个RSA密钥对,把公钥发给手机,手机弹窗让你确认信任。如果弹窗没点"允许",或者点了"否",这个信任关系就没建立。
处理办法是:
# 1. 杀掉ADB服务重新来过 adb kill-server adb start-server # 2. 重新连接手机,注意看手机屏幕上的授权弹窗 adb devices如果拔线重连多次还是unauthorized,那问题可能出在手机的"开发者选项"里曾经关闭过"USB调试"或者手机端存储了旧的密钥记录。部分品牌手机需要进入开发者选项,关闭"USB调试"再重新打开,才能清除旧的加密数据。
5037端口被占用
这个问题多发生在电脑上装了两个ADB工具(比如手机助手自带的ADB和官方platform-tools),两个版本的ADB服务进程抢同一个端口。
Windows下的排查方法:
netstat -ano | findstr "5037"找到PID后进任务管理器结束那个进程。如果找不到明确进程,直接用安全模式关闭所有可能占用5037的程序,再重启ADB。
注意:这个错误提示里出现的
could not read ok from adb server往往是在adb start-server时出现的,原因是老进程还在,新进程无法正常建立连接。务必先杀干净再启动。
5.3 设备厂商的定制化坑
不同手机厂商对ADB的"魔改"程度差别很大。我有一次用一加手机跑自动化脚本,前30分钟一切正常,某一步突然adb shell input tap完全没有反应,但adb devices显示设备状态正常。排查了半天,发现是ColorOS的系统权限管理把模拟点击当成了"高风险操作",需要手动在权限管理里授予ADB模拟点击的权限。
不同品牌的解锁姿势汇总:
- 三星:开发者选项开启"开发人员选项",再开启"监控"和"指针位置",USB调试授权一次后基本稳定
- 荣耀/华为(新版本):必须插上SIM卡并登录华为账号,否则开发者选项不保存
- 摩托罗拉:部分设备需要开启"允许模拟点击"权限
- 老款创维网络盒子:需要在工厂模式里开启ADB,方法不同型号差异极大,通常要按遥控器组合键
做得久了你会发现,一台手机上跑通的自动化脚本,换台手机可能要改一晚上。这不是脚本本身的问题,而是各家ROM对ADB权限管制的差异。
5.4 日志抓取与崩溃定位
热词里提到adb logcat 抓取日志,这是自动化脚本调试的核心手段。分享一个我常用的日志采集思路:
# 清空日志缓冲 adb logcat -c # 带时间戳启动App并采集指定进程的日志 adb shell am start -W -n com.example.app/.MainActivity adb logcat -v time -s com.example.app:V AndroidRuntime:E > app_log.txt & # 跑完自动化后,停止日志采集(Ctrl+C)看日志时,我主要关注三类异常:
FATAL EXCEPTION:程序崩溃,通常是空指针或资源找不到ANR in com.example.app:主线程卡死,多半是网络请求或数据库操作阻塞了UI线程SecurityException:权限问题,需要检查Shizuku有没有正确授权
对自动化脚本来说,SecurityException是最常见的失败原因。如果脚本是通过Shizuku执行命令,但某个特定操作提示无权限,多半是Shizuku权限被系统收回了,去Shizuku App里重启服务即可。
6. 一些私人经验和建议
做了这么久ADB自动化,最深的感触是:这套技能真正的分水岭不在于会敲几条命令,而在于能不能把命令组合成一套可靠的任务流程。命令是固定的,但流程里的每个延时、每次异常处理、每一步失败重试,才是自动化脚本的灵魂。
给刚开始接触的朋友几个建议:
第一,先把基础命令在命令行里跑熟,再写脚本。我见过太多人一上来就写Python脚本,一个adb devices都没在终端里手动执行过,结果脚本报错分不清是Python语法问题还是ADB命令问题。
第二,善用延时和重试机制,但不要滥用。每次操作之间加一秒钟延时能解决大部分偶发问题,但也会让脚本执行时间成倍增加。我一般把基础延时设为1到2秒,对页面加载时间长的场景单独做条件等待。
第三,脚本的关键步骤一定要打印日志。加一行print可能只花一秒,但出了问题能帮你省一小时的排查时间。
Shizuku和ADB的组合,短期内不会过时。Android系统每次迭代都在收紧权限管控,但ADB调试通道始终是开发者保留的"后门"。学会合理地利用这个通道,能帮你省下大量重复的机械操作时间。下一步我准备在这个基础上继续研究如何结合LLM做一些更智能的自动化决策,目前已经在看相关方向了。