1. 项目概述:为什么用 adblib + ADB Wi-Fi 控制 Android 9 开发板,而不是直接插线?
我第一次在客户现场调试一块基于axu15egp系列嵌入式处理器的 Android 9 开发板时,就踩进了“有线依赖”的坑里。那块板子被焊死在工业机柜最底层,USB口朝下、离最近的可操作空间有42厘米,每次拔插数据线都得拆三颗螺丝、垫两本手册、歪着脖子操作——更糟的是,客户明确要求“运行中禁止物理接触”,因为板子正驱动着产线上的PLC通信模块,热插拔导致过三次CAN总线中断。那天我蹲在机柜旁写了半小时adb shell命令,却连一次logcat都抓不到。直到回办公室翻文档,才意识到:Android 9(Pie)从系统层开始原生支持adb tcpip模式,配合成熟的adblib库,完全能绕过物理线缆,实现毫秒级指令下发和实时日志回传。这不是炫技,而是工业嵌入式场景下的刚需。
所谓“通过 adblib 使用 ADB Wi-Fi 控制 Android 9 开发板”,本质是把传统 USB ADB 的能力,平移到无线信道上执行。核心不是“连上Wi-Fi就行”,而是构建一条可信、低延迟、可编程的远程调试通道。adblib(Python生态中最稳定的ADB封装库)在这里扮演“协议翻译器”角色——它不处理Wi-Fi连接本身,而是把你的 Python 脚本指令(比如shell:input keyevent 82或pull /data/misc/wifi/WifiConfigStore.xml),精准打包成 ADB 协议帧,通过 TCP socket 发送给开发板上已监听的adbd进程。而 Android 9 的关键价值在于:它默认启用adbd的网络调试模式,且对adb tcpip命令的权限控制比 Android 8 更宽松(无需 root 即可执行adb tcpip 5555),这对开发板场景至关重要——你不可能为每块出厂固件刷入自定义 recovery。
这个方案真正解决的是三类人的问题:
- 嵌入式工程师:需要批量烧录固件、抓取内核 log、监控
/sys/class/thermal/温度节点,但开发板部署在密闭机箱或远端网关; - 自动化测试人员:用 Python 脚本驱动 UI 自动化(Appium 底层依赖 ADB),但测试机群无法布设 USB Hub;
- 产线运维人员:需远程重启服务、导出 crash dump、切换 debug 模式,但现场无工程师驻守。
它不适用于追求“零配置”的小白用户——你需要理解 ADB 的 client-server 架构、Wi-Fi 网络拓扑、以及 Android 9 的 SELinux 策略限制。但一旦跑通,你就能用 3 行 Python 代码完成过去要开 MobaXterm + 手动输入 17 条命令才能做的事。接下来我会拆解整个链路:从开发板端的 adbd 配置,到 PC 端的 adblib 调用,再到真实产线环境中的稳定性加固。
2. 核心技术点深度解析:Android 9 的 ADB Wi-Fi 机制与 adblib 的协议适配逻辑
2.1 Android 9 的 ADB Wi-Fi 工作原理:不是“开启热点”,而是重定向 ADB 流量
很多人误以为“ADB Wi-Fi”是让手机或开发板主动发射一个 Wi-Fi 热点供 PC 连接。这是典型误区。Android 9 的 ADB Wi-Fi 本质是TCP/IP 端口转发机制,其底层逻辑与 USB ADB 完全一致,只是传输层从 USB endpoint 换成了 TCP socket。理解这一点,是避免后续所有连接失败的根本。
ADB 协议采用 client-server 架构:PC 上的adb命令是 client,开发板上的adbd(Android Debug Bridge daemon)是 server。USB 模式下,client 通过 USB 设备枚举找到adbd的 endpoint 地址;Wi-Fi 模式下,client 必须知道adbd监听的IP 地址 + 端口号。Android 9 的adbd默认只监听127.0.0.1:5037(本地 loopback),这是安全设计——防止外网随意接入。要启用 Wi-Fi 调试,必须执行adb tcpip <port>命令,该命令会触发adbd停止监听 loopback,转而绑定到所有网络接口的指定端口(如0.0.0.0:5555)。注意:0.0.0.0不代表“开放给全世界”,而是“监听本机所有网卡的该端口”,实际访问权限仍由 Linux iptables 和 Android SELinux 共同控制。
这里有个关键细节:Android 9 的adbd在非 root 状态下,仅允许绑定到 5555 端口。这是 Google 为防止恶意应用滥用调试端口设定的硬性限制。如果你尝试adb tcpip 5556,会收到error: more than one device/emulator或error: device offline—— 因为adbd拒绝启动该端口监听。这个限制在system/core/adb/adb_main.cpp的should_use_tcpip_port()函数中有明确校验。所以,所有教程里写的“adb tcpip 5555”不是约定俗成,而是 Android 9 的强制规范。
再深一层:adbd绑定到0.0.0.0:5555后,它如何区分“合法 ADB client”?答案是ADB 密钥认证。首次通过 USB 连接时,PC 的adb会生成一对 RSA 密钥(~/.android/adbkey和~/.android/adbkey.pub),并将公钥发送给开发板,存入/data/misc/adb/adb_keys。之后每次 Wi-Fi 连接,adbd都会要求 client 提供私钥签名,验证通过才建立会话。这意味着:即使你知道开发板 IP 和 5555 端口,没有正确的 adbkey,依然无法连接。这也是为什么很多“一键开启 Wi-Fi ADB”的 App 在 Android 9+ 上失效——它们无法绕过密钥交换流程。
2.2 adblib 的核心价值:绕过 shell 解析,直击 ADB 协议二进制层
adblib(GitHub 仓库名pyside6-adblib,当前主流版本 1.0.12)不是简单的subprocess.Popen(['adb', 'shell', 'ls'])封装。它的核心竞争力在于跳过 shell 层,直接构造 ADB 协议包。这带来三个不可替代的优势:
第一,规避 shell 解析歧义。比如你想执行shell:ls /sdcard/Download/中文文件.txt,在传统方式下,subprocess会将整个字符串交给 PC 的 shell 解析,而 Windows cmd 和 Linux bash 对 Unicode 路径的处理差异巨大,极易出现No such file or directory错误。adblib 则直接将ls /sdcard/Download/中文文件.txt作为 payload,通过 ADB 协议的CNXN(connect)、OPEN(open shell channel)、WRTE(write data)三步帧序列发送,开发板端的adbd收到后直接交由execv()执行,完全不经过sh -c解析,中文路径天然兼容。
第二,实现原子化指令流。传统adb shell每次调用都是独立进程,启动sh解释器、加载 libc、fork 子进程,耗时约 80-120ms。adblib 复用同一个 TCP 连接,连续发送多个WRTE包,adbd在单个 shell session 内顺序执行,10 条命令总耗时可压至 200ms 内。我在测试imx6ull开发板时,用 adblib 批量读取 50 个传感器寄存器(shell:cat /sys/bus/i2c/devices/1-0018/iio:device0/in_voltage0_raw×50),耗时 1.3 秒;用subprocess调用 50 次adb shell,耗时 6.8 秒——差了 5 倍。
第三,支持细粒度错误捕获。ADB 协议规定,每个WRTE包对应一个OKAY或FAIL响应帧。adblib 将FAIL帧中的错误码(如protocol fault、device not found)映射为 Python 异常(AdbCommandFailureException),并附带原始错误消息。而subprocess只能捕获adb命令的 exit code(通常是 0 或 1),丢失具体失败原因。例如,当开发板因 SELinux 限制拒绝执行setprop sys.usb.config adb时,adblib 抛出AdbCommandFailureException: setprop failed: permission denied,而subprocess只返回exit code 1,你得去翻adb logcat才能找到线索。
提示:adblib 的
AdbClient类初始化时,host参数填开发板 IP(如'192.168.1.100'),port参数必须是5555(Android 9 强制),default_timeout_ms建议设为5000(5秒)。不要试图用AdbClient(host='localhost', port=5555)—— 这会让 adblib 去连接本机的adb server,而非开发板的adbd。
2.3 Android 9 开发板的特殊约束:SELinux、adbd 启动时机与网络隔离
Android 9 的 SELinux 策略对adbd的权限做了显著收紧,这是区别于 Android 7/8 的最大坑点。在axu15egp系列或t113开发板上,即使你成功执行adb tcpip 5555,仍可能遇到error: device unauthorized或shell command not found。根本原因在于 SELinux 的domain和type限制。
adbd进程在 Android 9 中运行于adbddomain,其可访问的文件类型(file type)被严格限定。例如,默认策略禁止adbd读取/data/misc/wifi/下的配置文件(wifi_config_store.xml),因为该目录的 type 是wifi_data_file,而adbddomain 没有read_file_perms权限。当你用 adblib 执行shell:cat /data/misc/wifi/WifiConfigStore.xml时,adbd会静默失败,返回空响应。解决方案不是关闭 SELinux(生产环境严禁),而是向设备厂商索要sepolicy补丁,或在init.rc中添加allow adbd wifi_data_file:file { read open getattr }规则。
另一个致命陷阱是adbd 启动时机。Android 9 的adbd默认在early-init阶段启动,早于netd(网络守护进程)。这意味着:如果开发板使用 DHCP 获取 IP,adbd绑定0.0.0.0:5555时,网卡可能尚未获得 IP 地址,导致adbd实际监听的是0.0.0.0,但 PC 无法通过任何 IP 访问。实测发现,在radxa rock 5b+开发板上,DHCP 获取 IP 平均耗时 2.3 秒,而adbd在 boot 第 1.8 秒就完成了tcpip绑定。解决方法是在init.rc中,将adbd的启动 service 依赖netd:
service adbd /sbin/adbd class main user shell group shell adb disabled # 关键:等待 netd 就绪 on property:net.dns1=* enable adbd这样adbd会在 DNS 设置完成后才启动,确保网卡已配置好 IP。
最后是网络隔离问题。很多工业开发板(如粤嵌gec6818)默认启用iptables,规则链中包含DROP所有非白名单端口的 INPUT 流量。即使adbd绑定了0.0.0.0:5555,防火墙也会拦截 PC 的 TCP SYN 包。检查命令:adb shell su -c 'iptables -L INPUT -n'。若看到REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited,说明防火墙生效。临时放行:adb shell su -c 'iptables -I INPUT -p tcp --dport 5555 -j ACCEPT'。长期方案是修改init.rc,在netd启动后自动插入该规则。
3. 实操全流程:从开发板配置到 adblib 脚本编写,含完整参数计算与避坑清单
3.1 开发板端配置:四步完成 ADB Wi-Fi 就绪(适配 Android 9)
第一步:确认开发板已启用 ADB 调试并获取初始 USB 连接
这是不可跳过的前提。Android 9 开发板的 ADB 调试开关通常藏在“关于设备”中:连续点击“版本号”7次,激活开发者选项;进入“开发者选项”,打开“USB 调试”。此时用 USB 线连接 PC,执行adb devices,应看到设备序列号(如1234567890ABCDEF)状态为device。若显示unauthorized,需在开发板弹出的授权对话框点“允许”。这一步的本质是完成ADB 密钥交换,将 PC 的adbkey.pub写入开发板/data/misc/adb/adb_keys。没有这步,Wi-Fi 连接必然失败。
第二步:执行adb tcpip 5555并验证端口绑定
在 PC 终端执行:
adb tcpip 5555 # 输出:restarting in TCP mode port: 5555此命令会重启adbd进程,并使其监听0.0.0.0:5555。立即拔掉 USB 线,执行adb kill-server清除本地 adb server 缓存。然后,用开发板的 Wi-Fi IP(假设为192.168.1.100)连接:
adb connect 192.168.1.100:5555 # 输出:connected to 192.168.1.100:5555 adb devices # 输出:192.168.1.100:5555 device若连接失败,90% 的原因是开发板未获取到 IP。此时需手动查询:adb shell ip addr show wlan0 | grep "inet "(wlan0 是常见 Wi-Fi 接口名,也可能是eth0或usb0)。若输出为空,说明 Wi-Fi 未连接,需先配置 Wi-Fi:adb shell am startservice -n com.android.settings/.wifi.WifiService(不推荐)或更可靠的方式——用adb shell进入后执行su -c 'wpa_cli -i wlan0 scan'扫描网络。
第三步:固化 Wi-Fi ADB 配置(避免重启失效)
Android 9 的adb tcpip设置在设备重启后会丢失,因为adbd默认恢复为 USB 模式。要永久生效,需修改init.rc。找到init.rc文件(通常在/system/etc/init/或/vendor/etc/init/),添加以下 service:
# 在 init.rc 末尾添加 service adbd_wificonnect /system/bin/sh -c "while true; do sleep 10; if [ -f /proc/net/tcp ]; then echo 'adbd ready'; else continue; fi; done" class late_start user root group root oneshot # 关键:在 network 就绪后执行 on property:sys.boot_completed=1 exec /system/bin/sh -c "setprop service.adb.tcp.port 5555 && stop adbd && start adbd"这段脚本的逻辑是:当系统启动完成(sys.boot_completed=1),执行setprop设置 TCP 端口,然后重启adbd。setprop命令会写入ro.adb.tcp.port属性,adbd进程启动时会读取该属性并自动绑定。实测在zynq7100 开发板上,此方案使 Wi-Fi ADB 在 100 次重启中 100% 生效。
第四步:验证 SELinux 策略与防火墙
执行adb shell getenforce,输出应为Enforcing(正常)。若为Permissive,说明 SELinux 被禁用,安全性降低。接着检查adbd权限:
adb shell su -c 'ls -Z /system/bin/adbd' # 正确输出:u:object_r:adbd_exec:s0 /system/bin/adbd若 type 不是adbd_exec,需重新编译 sepolicy。最后检查防火墙:
adb shell su -c 'iptables -L INPUT -n | grep 5555' # 应看到:ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:5555若无此行,手动添加:adb shell su -c 'iptables -I INPUT -p tcp --dport 5555 -j ACCEPT'。
3.2 PC 端环境搭建:adblib 安装、连接测试与基础脚本模板
安装 adblib
强烈建议使用pip install adblib==1.0.12(指定版本)。adblib 1.1.0+ 引入了异步 API,但在 Android 9 开发板上偶发ConnectionResetError,1.0.12 是经过千次产线测试的稳定版。安装后验证:
from adblib import AdbClient client = AdbClient(host='192.168.1.100', port=5555, default_timeout_ms=5000) print(client.get_state()) # 应输出 'device'基础连接测试脚本
以下脚本完成三件事:连接开发板、执行 shell 命令、拉取文件。这是所有自动化任务的起点:
from adblib import AdbClient import time # 初始化客户端 client = AdbClient( host='192.168.1.100', # 开发板 IP port=5555, # Android 9 强制端口 default_timeout_ms=5000 ) try: # 1. 检查设备状态 state = client.get_state() print(f"设备状态: {state}") # 2. 执行 shell 命令:获取 CPU 温度(适配 imx6ull) cpu_temp = client.shell("cat /sys/class/thermal/thermal_zone0/temp") print(f"CPU 温度: {int(cpu_temp.strip()) / 1000}°C") # 3. 拉取日志文件(注意:adblib 的 pull 是同步阻塞的) client.pull("/data/anr/traces.txt", "./traces.txt") print("ANR 日志已拉取") except Exception as e: print(f"操作失败: {e}") finally: client.close() # 必须显式关闭,否则 socket 泄漏关键细节:client.pull()的第一个参数是开发板上的绝对路径,第二个是 PC 上的本地路径。路径中不能有空格或中文(adblib 1.0.12 对 UTF-8 路径支持不完善),建议用client.shell("ls /data/anr/")先确认文件存在。
批量设备管理脚本
产线常需同时控制多块开发板(如esp32s3开发板硬件介绍中提到的集群)。以下脚本支持并发连接:
from adblib import AdbClient from concurrent.futures import ThreadPoolExecutor, as_completed import json # 设备列表:IP + 设备标识 devices = [ {'ip': '192.168.1.101', 'name': 'AXU15EGP-01'}, {'ip': '192.168.1.102', 'name': 'AXU15EGP-02'}, {'ip': '192.168.1.103', 'name': 'T113-01'} ] def check_device(device_info): try: client = AdbClient( host=device_info['ip'], port=5555, default_timeout_ms=3000 ) # 获取设备型号和 Android 版本 model = client.shell("getprop ro.product.model").strip() version = client.shell("getprop ro.build.version.release").strip() client.close() return { 'ip': device_info['ip'], 'name': device_info['name'], 'model': model, 'version': version, 'status': 'online' } except Exception as e: return { 'ip': device_info['ip'], 'name': device_info['name'], 'status': 'offline', 'error': str(e) } # 并发检查 results = [] with ThreadPoolExecutor(max_workers=5) as executor: future_to_device = {executor.submit(check_device, d): d for d in devices} for future in as_completed(future_to_device): results.append(future.result()) # 输出 JSON 报告 print(json.dumps(results, indent=2, ensure_ascii=False))此脚本将 10 台设备的探测时间从串行的 30 秒压缩至并行的 6 秒。max_workers=5是经验值:超过 5 个并发连接,adbd的 TCP backlog 队列(默认 128)可能溢出,导致部分连接超时。
3.3 高阶应用:UI 自动化、固件烧录与实时日志监控
UI 自动化:绕过 Appium,用 adblib 直控 AccessibilityService
Appium 底层依赖 ADB,但增加了中间层开销。对于android studio agp 9项目生成的 APK,可直接用 adblib 模拟点击:
# 模拟点击坐标 (500, 800),适配 1080p 屏幕 client.shell("input tap 500 800") # 输入文本(需先 focus 到 EditText) client.shell("input text 'HelloWorld'") # 滑动屏幕(从 (200,1000) 到 (200,300),持续 500ms) client.shell("input swipe 200 1000 200 300 500")关键技巧:input命令的坐标系是屏幕像素,需根据开发板分辨率动态计算。获取分辨率:client.shell("wm size").split()[-1](输出如1080x1920)。若开发板是imx6ull开发板在屏幕终端中文显示乱码场景,input text会失效,改用input keyevent发送 Unicode 码点(需提前查表)。
固件烧录:用 adblib 替代 fastbootfastboot需要设备进入 bootloader 模式,而很多工业开发板(如合众恒跃瑞芯微3506开发板)锁定了 bootloader。此时可利用adb sideload:
# 将固件 zip 推送到 /cache/recovery/ client.push("./firmware.zip", "/cache/recovery/update.zip") # 触发 recovery 升级 client.shell("reboot recovery") # 注意:此命令会断开连接,需在 reboot 前设置重连逻辑adb sideload的优势是无需解锁 bootloader,且升级过程可被adb logcat实时监控。
实时日志监控:构建轻量级 logcat 代理adb logcat命令在长连接中易断开。adblib 提供logcat方法,但需手动处理流:
import threading def logcat_monitor(client, tag_filter=""): """实时打印 logcat,支持过滤""" # 启动 logcat 流 stream = client.logcat( clear=True, # 清空缓冲区 filter_specs=[tag_filter] if tag_filter else None, format="brief" # 输出格式:level/tag/pid/pid/message ) def read_log(): for line in stream: print(f"[LOG] {line.decode('utf-8').strip()}") # 在新线程中读取,避免阻塞主线程 thread = threading.Thread(target=read_log, daemon=True) thread.start() return thread # 使用示例 monitor_thread = logcat_monitor(client, "ActivityManager") time.sleep(30) # 监控 30 秒 # monitor_thread 会自动退出,无需 join此方案比adb logcat -v brief | grep ActivityManager更稳定,因为 adblib 的logcat方法直接复用 ADB 的logchannel,不受 shell 缓冲区影响。
4. 常见问题与排查技巧实录:来自 37 个产线项目的故障速查表
4.1 连接类问题:90% 的失败源于网络层或密钥错配
| 现象 | 根本原因 | 排查步骤 | 解决方案 |
|---|---|---|---|
adb connect 192.168.1.100:5555返回failed to connect to 192.168.1.100:5555 | 开发板未监听 5555 端口,或防火墙拦截 | 1.adb shell netstat -tuln | grep 55552. adb shell su -c 'iptables -L INPUT -n' | 若 netstat 无输出,执行adb tcpip 5555;若 iptables 有 DROP 规则,插入 ACCEPT |
adb devices显示192.168.1.100:5555 offline | ADB 密钥未同步,或adbd未授权 | 1.adb shell cat /data/misc/adb/adb_keys2. cat ~/.android/adbkey.pub | 比对两文件内容,若不一致,删除开发板/data/misc/adb/adb_keys,重新 USB 连接授权 |
adblib.AdbCommandFailureException: device not found | adblib 初始化时 IP/端口错误,或开发板休眠 | 1.ping 192.168.1.1002. adb shell dumpsys power | grep "mWakefulness" | 若 ping 通但mWakefulness=Asleep,执行adb shell input keyevent 26(电源键)唤醒 |
注意:
adb shell netstat在 Android 9 中可能不可用(busybox 未包含)。替代方案:adb shell su -c 'lsof -i :5555'或adb shell cat /proc/net/tcp \| grep ':15B3'(5555 的十六进制是15B3)。
4.2 权限类问题:SELinux 与 Android 权限模型的双重限制
问题:client.shell("settings put global airplane_mode_on 1")报错Security exception
这是 Android 9 的Settings.Global写入权限限制。adbd进程默认无WRITE_SECURE_SETTINGS权限。解决方案不是给adbd加权限(违反最小权限原则),而是改用adb shell的am命令:
# 正确:通过 Activity Manager 发送广播 client.shell("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true")am broadcast由 system_server 进程执行,拥有更高权限。
问题:client.pull("/data/data/com.example.app/databases/app.db", ...)失败,提示Permission denied/data/data/目录受 Android 沙箱保护。adbd无法直接读取其他应用的数据目录。解决方案是:
- 在 App 中添加
android:debuggable="true"(仅调试版); - 用
run-as命令切换到目标应用 UID:
client.shell("run-as com.example.app cat databases/app.db > /sdcard/app.db") client.pull("/sdcard/app.db", "./app.db")run-as是 Android 提供的沙箱穿透工具,比 root 更安全。
4.3 性能与稳定性问题:高并发下的资源瓶颈与超时陷阱
问题:并发连接 20 台开发板时,部分连接超时
根源是adbd的 TCP backlog 队列满。Android 9 的adbd默认listen()backlog 为 128,但每个连接占用一个 socket 描述符。当并发数 >128,新连接被内核拒绝。
实测数据:在axu15egp板上,128 连接耗尽内存 18MB,CPU 占用 42%。
优化方案:
- 降低
max_workers至 10(ThreadPoolExecutor); - 为每个
AdbClient设置default_timeout_ms=10000,避免短时抖动断连; - 添加重试逻辑:
from tenacity import retry, stop_after_attempt, wait_exponential @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10)) def safe_shell(client, cmd): return client.shell(cmd)问题:client.logcat()流突然中断,无异常抛出
这是 ADB 协议的DATA帧丢失所致。Android 9 的adbd在高负载下可能丢弃 logcat 数据包。
终极方案:改用adb logcat -b main -b system -b events -v threadtime > log.txt重定向到文件,再用client.pull()拉取:
# 启动后台 logcat client.shell("logcat -b main -b system -b events -v threadtime > /sdcard/logcat.log &") # 等待 10 秒 time.sleep(10) # 拉取日志 client.pull("/sdcard/logcat.log", "./logcat.log")虽然牺牲实时性,但 100% 可靠。
4.4 开发板特有问题:针对热门型号的定制化修复
imx6ull开发板中文乱码:
现象:client.shell("echo '测试'")返回乱码。
原因:adbd的 locale 为C,不支持 UTF-8。
修复:adb shell su -c 'export LANG=en_US.UTF-8; echo 测试',但 adblib 不支持环境变量传递。
替代方案:用printf输出 Unicode 码点:
# “测” 的 Unicode 是 U+6D4B,UTF-8 编码为 \xe6\xb5\x8b client.shell("printf '\xe6\xb5\x8b\xe8\xaf\x95' > /sdcard/test.txt")vscode软件怎么连接开发板:
VS Code 的Android Debug Bridge插件不支持 adblib。正确做法是:
- 在 VS Code 中安装
Remote - SSH插件; - 将开发板配置为 SSH 服务器(需
dropbear); - 用
adb connect建立 ADB 通道,再用 VS Code 的终端执行adb shell。
adblib 与 VS Code 无直接集成,它是 Python 库,需在 VS Code 的 Python 环境中调用。
android studio下载固件后无法部署到开发板:
Android Studio 默认使用adb install,但开发板可能禁用INSTALL_PACKAGES权限。
绕过方案:
# 在 Android Studio 的 Run Configurations 中,修改 "Before launch" 步骤: # 添加 "Run External tool" -> Command: adb push app-debug.apk /data/local/tmp/ # 再添加 "Run External tool" -> Command: adb shell pm install -r /data/local/tmp/app-debug.apkpm install由 PackageManagerService 执行,权限高于adb install。
5. 工程化实践:将 adblib 集成到 CI/CD 流水线与产线监控大屏
5.1 Jenkins 流水线集成:自动化固件回归测试
在android studio项目构建完成后,