用途
本技能解决 Android 测试产物在设备与主机之间的搬运问题,覆盖 adb pull / adb push 的语法与权限规则,以及 androidx.test:services 的 TestStorage API 用于按测试路由产物的方式。适用情形包括:测试产生的产物(截图、JSON、采集到的数据库)需要落到 CI 的构建输出;adb pull 拉取 /data/data/<pkg>/ 报 Permission denied 且不清楚 run-as 的存在;二进制文件(PNG、MP4、SQLite DB)在主机上出现字节错乱;大体积推送缓慢需要压缩参数;测试应用把截图写到 /sdcard/Android/data/<pkg>/files/ 且需确认 API 30+ 下是否可拉取;团队从“写 /sdcard、主机拉取”向 TestStorage API 迁移。
本技能不负责:截图或录屏本身的采集、日志提取、APK 安装卸载、设备输入驱动以及完整的 CI 编排。
前置条件
adb get-state 返回 device;使用 run-as 时目标 APK 的合并清单需为 android:debuggable="true"(debug 构建变体默认如此);使用 -z brotli/zstd 需主机 platform-tools 30+ 且设备 adbd 为 Android 11 及以上;使用 TestStorage 需 androidx.test:services 依赖,并在 am instrument 时传入 -e useTestStorageService true。
主要能力
拉取与推送采用对称语法:adb pull <remote> [<local>](设备到主机,默认当前目录)与 adb push <local> <remote>(主机到设备)。现代参数(platform-tools ≥ 30)包括:-z <algo> 启用压缩,算法可为 any / none / brotli / lz4 / zstd;-Z 关闭压缩;--sync(仅推送)仅推送时间戳不同的文件,实现快速增量更新;-a(仅拉取)保留文件时间戳与模式;-n 空跑;-q 静默进度信息。旧版 adb pull -p(显示进度)现已是空操作,现代 adb 默认显示进度,-q 可静默,不建议在 CI 脚本中依赖 -p。
路径权限规则
这是最容易导致拉取失败的部分。
/data/local/tmp/:shell 用户可读写,无需应用权限流程,可作为一切内容的暂存目录。注意应用以 u0_aXX 而非 shell 运行,应用读取 shell 写入的文件需 chmod 0644 且父目录全局可读。
/sdcard/(即 /storage/emulated/0/,有时为 /storage/self/primary/):公共/外部存储。API 30+ 收紧作用域存储后,adb 的 shell 用户仍保有 FUSE/sdcardfs 访问,因此 adb push /sdcard/... 与 adb pull /sdcard/... 在测试中仍可用;应用侧写入在 API 30+ 受 MediaStore 限制。
/data/data/<pkg>/:由应用 UID 拥有、目录权限 0700。裸 adb pull 在正式用户构建上会失败,仅有两条可行路径:在 debuggable 构建上使用 run-as <pkg>,或在 userdebug/eng 构建上使用 adb root。
/sdcard/Android/data/<pkg>/files/:应用的外部私有存储,应用通过 Context.getExternalFilesDir(null) 写入,因位于 FUSE 模拟的 SD 卡下而可由 adb shell 读取。在 Android 11+ 上是“写产物、CI 拉取”阻力最小的路径。
使用方式:run-as
run-as <pkg> 是 Android 自 Gingerbread 起的 setuid 助手,仅当 APK 合并清单含 android:debuggable="true" 时才把 shell 切入应用 UID。用法为 adb shell run-as <pkg> <cmd>。
文本文件抓取可用 adb shell run-as com.example.app cat databases/main.db > main.db,但对二进制文件会因 PTY 转换损坏字节。目录、二进制文件或任何含 \n 字节的内容必须使用 exec-out(二进制干净的 stdout)并通过 tar 管道:adb exec-out run-as com.example.app tar cf - databases/ | tar xf - -C ./pulled。单文件二进制抓取用 adb exec-out run-as com.example.app cat databases/main.db > main.db。列出文件用 adb shell run-as com.example.app ls -lR files/。
run-as 不能做三件事:对 release/非 debuggable 构建无效,会打印 "Package is not debuggable";不能切换到系统 UID;不能自行跨越配置文件边界,必要时用 --user N。
常见测试产物路径
Gradle 标准“额外输出”目录:app/build/outputs/connected_android_test_additional_output/<flavor><Variant>/connected/<deviceId>/;JUnit 风格 XML 结果:app/build/outputs/androidTest-results/connected/<flavor><Variant>/TEST-*.xml;HTML 报告:app/build/reports/androidTests/connected/。
现代化产物 API:TestStorage
传统做法是写 /sdcard 再由 CI 脚本拉取;现代做法通过 androidx.test.services.storage.TestStorage 与 useTestStorageService 标志路由。接线方式:添加 androidTestImplementation("androidx.test:runner:1.7.0") 与 androidTestUtil("androidx.test.services:test-services:1.6.0"),并在 am instrument 参数(或 Gradle 的 testInstrumentationRunnerArguments)中传入 -e useTestStorageService true。测试内通过 androidx.test.platform.io.PlatformTestStorageRegistry.getInstance() 获取实例,用 storage.openOutputFile("screenshots/fail-${test.methodName}.png") 写出。其优势包括:产物落入 TestStorageService 管理的目录而非 /sdcard 本身;Gradle 的 connectedAndroidTest 会自动拉取到 app/build/outputs/connected_android_test_additional_output/;按测试命名空间由 runner 处理;与 Test Orchestrator 的 clearPackageData: 'true' 兼容;非 Gradle 流水线可通过 /sdcard/googletest/test_outputfiles/... 访问相同布局。
推荐做法与规则
二进制抓取必须使用 adb exec-out run-as 而非 adb shell run-as,避免 PTY 转换损坏;目录抓取必须用 exec-out + tar 管道,没有等价的 adb pull 方式;脚本化 run-as 前必须确认构建为 debuggable;API 30+ 应用写入产物应优先使用 Context.getExternalFilesDir(null) 对应的 /sdcard/Android/data/<pkg>/files/ 而非任意 /sdcard 目录;新测试基础设施应优先 TestStorage;不要依赖 adb pull -p;不要假设不经 run-as 就能 pull /data/data/<pkg>/;重复推送大量 fixture 建议 --sync -z brotli;测试隔离与产物保留可组合 Test Orchestrator + clearPackageData: 'true' + useTestStorageService: 'true'。
校验要点
脚本不得直接 adb pull /data/data/<pkg>/...;二进制拉取使用 exec-out;应用写入产物落在 Context.getExternalFilesDir(...);CI 归档 app/build/outputs/connected_android_test_additional_output/**/*;重复推送使用 --sync;对每个拉取产物用 file 确认类型正确(无 CRLF 损坏);release APK 不含尝试从 /data/data/<pkg>/ 拉取的测试路径。
限制
具体实现效果、性能差异与计费情况资料未提供;本技能不覆盖产物采集本身、日志提取、APK 安装与应用驱动等相邻范畴。