yaml
# 系列推荐
[ubuntu编译android12源码](https://mp.weixin.qq.com/s/jR6vpjvk6TSkFmGt3m8cwg)
[使用AndroidStudio阅读源码](https://mp.weixin.qq.com/s/PbzNo64ARHJCv_RxCc20-Q)
[Aosp开发神器Cuttlefish](https://mp.weixin.qq.com/s/94q8ldtVQzxHE30cDUr_5g)
[Aosp源码学习之目录介绍](https://mp.weixin.qq.com/s/kg9Tq5hPdVbyiCdxl4TlHA)
[Aosp之mk文件简介](https://mp.weixin.qq.com/s/cyPFvQ6IeCRXig12vllipQ)
[AOSP实战之bp文件介绍](https://mp.weixin.qq.com/s/rggXZIGccv1UuzRmObSDgQ)
[Aosp15之添加自己的产品Product](https://mp.weixin.qq.com/s/A8p5CBjErcMKS7Cts636wQ)
[Aosp之编译命令介绍](https://mp.weixin.qq.com/s/JTEQgp9PJD_8ieX5GGBRZQ)
[Aosp之添加C++可执行程序](https://mp.weixin.qq.com/s/lahaVLAMLjGiHhw8S-t0Lw)
[Aosp之添加C++动态库](https://mp.weixin.qq.com/s/ZlThBG4W7Y_tNhBxjYMB5Q)
[SEAndroid理论基础](https://mp.weixin.qq.com/s/inY7KfYXnctsaEW61bkyHA)
[Aosp实战之SEAndroid实战](https://mp.weixin.qq.com/s/RcUBB3msDbv6oyUe5VGlNw)
今天我们来回忆一下Aosp开发常用的ADB命令。。。
## 一、设备连接与管理
### 1.1 基本连接
```bash
adb devices # 查看已连接设备
adb devices -l # 带详情(型号、序列号、usb 口)
```
正常输出:
```
List of devices attached
emu32a08 device product:aosp_x86_64 model:Android_SDK_built_for_x86_64
```
**状态含义**:
| 状态 | 含义 | 怎么办 |
| :------------- | :-------------- | :----------------------------------------- |
| `device` | 正常可用 | 直接用 |
| `offline` | 连上了但没就绪 | 拔插 USB;`adb kill-server && adb devices` 重来 |
| `unauthorized` | 手机上弹了授权框没点 | 看手机屏幕,点"允许 USB 调试" |
| `no devices` | 没检测到 | 检查开发者选项里 USB 调试开了没;换线换口 |
| `recovery` | 设备在 recovery 模式 | 只能用部分命令(logcat 可用) |
| `bootloader` | 设备在刷机模式 | adb 不可用,要用 fastboot |
### 1.2 多台设备同时插着
```bash
adb devices -l # 先看序列号
adb -s R58M123ABC shell # 用 -s 指定设备
adb -s R58M123ABC install app.apk
```
### 1.3 无线连接(手机不插线)
**传统方式(需要先用一次 USB)**:
```bash
adb tcpip 5555 # 让 adbd 监听网络端口
adb connect 192.168.1.100:5555 # 连手机 IP(手机设置里看)
adb devices # 确认出现 ip:5555 device
# 用完切回 USB 模式
adb usb
```
**Android 11+ 无线配对(完全不用线)**:
```bash
# 手机:设置 → 开发者选项 → 无线调试 → 使用配对码配对设备
adb pair 192.168.1.100:配对端口 # 输入手机上显示的 6 位配对码
adb connect 192.168.1.100:连接端口 # 连接端口和配对端口不一样!
```
### 1.4 等设备就绪(刷机/重启后必备)
```bash
adb wait-for-device # 阻塞等设备出现
adb wait-for-device shell getprop sys.boot_completed # 等 1 才算开机完成
```
刷完机别急着 `adb shell`------adbd 起来了不代表系统开机完成,用上面第二条判断。
### 1.5 重启全家桶
```bash
adb reboot # 重启
adb reboot recovery # 进 recovery(卡死救砖用)
adb reboot bootloader # 进 fastboot(刷机用)
adb reboot edl # 进 9008 深刷模式(救砖终极手段,高危)
```
---
## 二、安装卸载 App
### 2.1 安装
```bash
adb install app.apk # 基本安装
adb install -r app.apk # 覆盖安装(保留数据)
adb install -r -d app.apk # 允许降级安装(新版本装回旧版本)
adb install -t app.apk # 允许安装测试包(testOnly 的 apk 必须加)
adb install -g app.apk # 安装时直接授予所有权限
adb install --bypass-low-target-sdk-block app.apk # 安装 targetSdk 过低的包(Android 14+ 默认拦截)
```
**安装失败先看报错尾巴**,常见 `INSTALL_FAILED_*`
### 2.2 卸载
```bash
adb uninstall com.example.app # 普通卸载
adb uninstall -k com.example.app # 卸载但保留数据和缓存(-k)
adb shell pm uninstall --user 0 com.example.app # 给当前用户卸载(可卸系统内置 App,不删文件)
adb shell pm uninstall --user 0 com.miui.player # 例:卸掉厂商预装音乐(慎用)
```
> `pm uninstall --user 0` 是"软卸载":对当前用户隐藏,恢复出厂能回来,比 root 删文件安全得多。删系统 App 前先 `adb shell pm list packages -s` 确认包名。
### 2.3 查 App 信息
```bash
adb shell pm list packages # 所有包名
adb shell pm list packages -3 # 只看第三方 App
adb shell pm list packages -s # 只看系统 App
adb shell pm list packages | grep video # 过滤
adb shell pm path com.example.app # 查 apk 安装在哪个路径
adb shell dumpsys package com.example.app | grep -E "versionName|versionCode|firstInstallTime" # 详见第七章
```
**提取已安装的 apk**(逆向/备份常备):
```bash
adb shell pm path com.example.app
# 输出: package:/data/app/~~xxxx==/com.example.app-yyyy==/base.apk
adb pull /data/app/~~xxxx==/com.example.app-yyyy==/base.apk ./app.apk
```
---
## 三、操控 App
### 3.1 启动 / 停止
```bash
adb shell am start -n com.example.app/.MainActivity # 按 包名/Activity 启动
adb shell monkey -p com.example.app -c android.intent.category.LAUNCHER 1 # 不知道 Activity 名?用 monkey 模拟点击桌面图标
adb shell am force-stop com.example.app # 彻底杀掉进程
adb shell am kill com.example.app # 温和杀(等价于系统回收,会被重启)
```
启动用 URL/deep link:
```bash
adb shell am start -a android.intent.action.VIEW -d "https://example.com"
adb shell am start -a android.intent.action.VIEW -d "myapp://page/detail?id=123" # 测试 deep link
```
### 3.2 清数据("重置"一个 App)
```bash
adb shell pm clear com.example.app # 清全部数据+权限,等同设置里"清除存储"
```
调试 App 的祖传操作:"它坏了" → `pm clear` → 重新试 → 还坏 → 抓日志。
### 3.3 权限管理
```bash
adb shell pm list permissions -d -g # 看危险权限分组
adb shell pm grant com.example.app android.permission.CAMERA # 手动授权
adb shell pm revoke com.example.app android.permission.CAMERA # 收回权限
adb shell dumpsys package com.example.app | grep -A 20 "runtime permissions" # 查当前权限状态(详见第七章)
```
测试"拒绝权限后的降级表现",不用在设置里点来点去,`revoke` 一步到位。
### 3.4 广播与服务
```bash
adb shell am broadcast -a android.intent.action.TIME_SET # 发广播
adb shell am startservice -n com.example.app/.MyService # 启动服务(targetSdk 26+ 对后台服务有限制,可能失败)
adb shell dumpsys activity services com.example.app # 查 App 的服务运行状态(详见第七章)
adb shell dumpsys activity receivers com.example.app # 查注册的广播接收器(详见第七章)
```
---
## 四、文件传输
### 4.1 基本操作
```bash
adb push local.png /sdcard/Pictures/ # 电脑 → 手机
adb pull /sdcard/DCIM/Camera ./camera # 手机 → 电脑(整个目录也行)
adb pull /sdcard/DCIM/Camera/img.jpg # 单个文件
```
### 4.2 权限规则(少踩坑)
| 目标路径 | shell 用户能直接写吗 | 说明 |
| :------------------------------- | :------------ | :---------------- |
| `/sdcard/` | ✅ | 随便读写 |
| `/data/local/tmp/` | ✅ | shell 的自留地,临时文件放这 |
| `/data/anr/`、`/data/tombstones/` | ⚠️ root 才行 | 崩溃/ANR 记录在这 |
| `/system/`、`/vendor/` | ❌ 只读 | 需要 root + remount |
root 机(userdebug 版)改系统文件:
```bash
adb root && adb remount
adb push framework.jar /system/framework/
adb reboot
```
### 4.3 传输二进制一定要用 exec-out
```bash
adb exec-out screencap -p > screen.png # ✅ 正确
adb shell screencap -p > screen.png # ❌ 可能坏(换行符被转义,图片打不开)
adb exec-out cat /proc/kmsg > kernel.log # 拉内核日志同理
```
---
## 五、日志 logcat
这是定位问题**第一现场**,值得花最多篇幅。
### 5.1 基本过滤(按优先级/标签)
```bash
adb logcat # 全部日志(信息量巨大)
adb logcat *:E # 只看 Error 及以上(W/E/F)
adb logcat -s MyAppTag # 只看某个 tag(-s 会屏蔽其他所有)
adb logcat -s MyAppTag:E ActivityTaskManager:I # 组合:我的 tag 报错级 + 系统调度信息级
adb logcat AndroidRuntime:E *:S # 经典组合:只看 Java 崩溃(AndroidRuntime + *:S 屏蔽其余)
```
优先级从低到高:`V` < `D` < `I` < `W` < `E` < `F`(Fatal)。日志行格式:
```
10-25 16:20:01.123 1234 5678 E MyAppTag: something failed
│ │ └─ 写日志的线程 id
│ └─ 进程 pid
└─ 时间
```
### 5.2 按进程过滤(只看你的 App)
```bash
adb shell pidof com.example.app # 先拿 pid,比如 12345
adb logcat --pid=12345 # 只看这个进程的日志
# 或一条组合(macOS/Linux):
adb logcat --pid=$(adb shell pidof com.example.app)
```
### 5.3 缓冲区:崩溃了先去 crash 缓冲区
日志存在 4 个独立缓冲区里,**进程死了日志还在**:
```bash
adb logcat -b crash # ⭐ 只看崩溃记录,闪退排查第一步
adb logcat -b main # 主缓冲(App 的 System.out.println、Log.x 都在这)
adb logcat -b system # 系统缓冲(AMS/WMS 等系统服务日志)
adb logcat -b events # 事件流水(am_proc_start、am_anr 等结构化事件,查 ANR 必看)
adb logcat -b all # 全部缓冲区
adb logcat -c # 清空(复现问题前先清,日志更干净)
```
### 5.4 格式与导出
```bash
adb logcat -v time # 带日期时间
adb logcat -v threadtime # 时间+pid+tid(推荐,默认就是它)
adb logcat -v color # 彩色(按级别着色)
adb logcat > crash_log.txt # 边看边存文件
adb logcat -d > crash_log.txt # -d = dump 当前已有日志后退出(不持续刷)
adb logcat -d -b crash # dump 崩溃缓冲区,复制给别人的标准姿势
```
**给别人报 bug 时**:`adb logcat -d -v threadtime -b crash -b main -b system > log.txt`,一条命令拿全证据。
### 5.5 实用组合拳
```bash
adb logcat | grep -i "exception|error|fatal" # 抓关键词(macOS/Linux)
adb logcat | grep -E "ANR|FATAL|tombstone" # 抓致命问题
adb logcat -s WifiHAL:V chatty:I # 关掉 chatty 抑制提示,看清真实日志
```
Windows 下没有 grep,用 `findstr`:`adb logcat | findstr /i "exception"`。
---
## 六、getprop:系统属性速查
系统把几百项配置以"属性"形式暴露出来,getprop 是读它们的标准工具:
```bash
adb shell getprop # 全部属性(几百条)
adb shell getprop ro.build.version.release # Android 版本,如 14
adb shell getprop ro.build.version.sdk # API 级别,如 34
adb shell getprop ro.build.fingerprint # 构建指纹(报 bug 必带)
adb shell getprop ro.product.model # 机型
adb shell getprop ro.product.cpu.abilist # 支持的 CPU 架构(arm64-v8a 等)
adb shell getprop ro.build.type # 编译变体:user / userdebug / eng
adb shell getprop ro.debuggable # 1 = adb root 可用
adb shell getprop sys.boot_completed # 1 = 开机完成
adb shell setprop debug.foo 1 # 设置属性(调试开关常用,重启失效)
adb shell setprop persist.log.tag V # persist. 开头的属性重启后仍保留
```
getprop是查询系统属性,而setprop是设置系统属性。
---
## 七、dumpsys 全解:所有系统状态查询汇总
dumpsys命令可以说是ADB中的重中之重了,可以说你的调试能力如何完全取决于你的dumpsys命令用得怎么样。。。
`dumpsys` 让系统里的每一个服务(AMS、WMS、电池、网络......)"交出体检报告"。
本章按用途分类:查界面(7.2)、查内存(7.3)、查 CPU/渲染(7.4)、查电量与后台(7.5)、查网络(7.6)、其余服务一张表(7.7)。
### 7.1 基本玩法:先学会"探索"
以activity为例,我们先来看看常用的玩法
```bash
adb shell dumpsys -l # ⭐ 列出设备上所有可查的服务名(几百个)
adb shell dumpsys activity # 查某个服务的完整报告(输出可能几千行)
adb shell dumpsys activity -h # 部分服务支持 -h 查看子命令帮助
adb shell dumpsys activity > ams.txt # 输出太大?先存文件再慢慢看
adb shell dumpsys activity | head -50 # 只看前 50 行(Overview 摘要通常就在开头)
adb shell dumpsys activity | grep -A 10 -B 2 "com.example.app" # 只关心自己的 App:grep 加上下文
```
三个探索技巧:
1. **报告开头都有 Overview/摘要段**,信息密度最高,先看开头再看细节;
2. **grep 必带上下文**(`-A 5` 看后 5 行、`-B 2` 看前 2 行),单行 grep 在 dumpsys 里等于白看;
3. 输出量太大时,存成文件用编辑器打开,比在终端里翻快 10 倍。
### 7.2 查界面与进程:activity / window
"App 白屏了","当前页面是谁"?"进程还在不在"?------都在这两个服务里。
```bash
# ---- activity:Activity 管理服务 ----
adb shell dumpsys activity top # ⭐ 当前栈顶 Activity 完整信息(含 View 层级、Fragment)
adb shell dumpsys activity activities # 所有任务栈、回退栈、每个 Activity 的状态
adb shell dumpsys activity services # 所有运行中的 Service
adb shell dumpsys activity services com.example.app # 只看某 App 的 Service
adb shell dumpsys activity broadcasts # 所有广播接收器 + 历史广播派发记录
adb shell dumpsys activity broadcasts | grep -A 3 com.example.app # 查谁收不到广播
adb shell dumpsys activity processes # 所有进程 + oom_adj 优先级(谁先被杀)
adb shell dumpsys activity providers # 所有 ContentProvider
adb shell dumpsys activity recents # 最近任务列表
# ---- window:窗口与显示 ----
adb shell dumpsys window | grep -E "mCurrentFocus|mFocusedApp" # ⭐ 焦点在哪个窗口
adb shell dumpsys window displays # 屏幕尺寸、旋转、DPI、显示区域
adb shell dumpsys window windows # 所有窗口的 Z 序(谁盖着谁)
```
**activity 输出怎么读**(`dumpsys activity activities`,重点两段):
```
Display #0 (activities from top to bottom):
Task id #123
mResumedActivity: ActivityRecord{... u0 com.example.app/.DetailActivity t123}
↑ 当前"活着的"(onResume 的)Activity,白屏问题先看这行
* ActivityRecord{... com.example.app/.MainActivity t123}
mState=STOPPED ↑ 每个 Activity 的生命周期状态:
RESUMED / PAUSED / STOPPED / DESTROYED
(卡在 PAUSED/STOPPED 不动 = 生命周期流程出了问题)
```
**判断 App 是否被杀/是否真的在前台**:
```bash
adb shell dumpsys activity processes | grep -B 1 -A 8 com.example.app
# 看 oom_adj 值:0 = 前台,几百 = 后台缓存进程(随时可被杀)
# 进程不在列表里 = 已被系统杀掉
```
**window 实战场景**:App 点了按钮没反应?第一嫌疑是"有个透明窗口/对话框抢了焦点"------`mCurrentFocus` 显示的不是你的 Activity 就是它。
这里可以列举附上排查手段:
```
# 第 1 步:看焦点现在在谁手上
adb shell dumpsys window | grep mCurrentFocus
# 正常输出应类似:mCurrentFocus=Window{abc1234 u0 com.example.app/com.example.app.MainActivity}
# 如果显示的是 Dialog、PopupWindow、Toast,或者根本是别的 App 的包名 → 就是它抢了焦点
# 第 2 步:焦点是弹窗?查它是谁弹的(按 uid 反查包名)
adb shell dumpsys window windows | grep -B 2 -A 5 "Window #"
# 在输出里找到焦点窗口的 u0=uid(如 u0a123 表示 uid 10123)
adb shell cmd package list packages --uid 10123
# 输出: package:com.suspect.app uid:10123 ← 找到真凶
# 第 3 步:验证猜想------按返回键关掉弹窗,再确认焦点回来了
adb shell input keyevent 4 # 返回键关弹窗
adb shell dumpsys window | grep mCurrentFocus # 焦点应回到你的 Activity
# 此时再点按钮,如果恢复了 → 实锤"弹窗抢焦点"
# 补充:点没反应也可能是"点击没生效"而不是"焦点被抢",用第 1 步确认后
# 若焦点正常,改查触摸事件是否到达:打开开发者选项 → 指针位置(显示触摸点),
# 或用 input tap 模拟点击对比手动点击的差异,排除触摸屏硬件/坐标偏移问题
```
### 7.3 查内存:meminfo
```bash
adb shell dumpsys meminfo com.example.app # 按 App 查
adb shell dumpsys meminfo 12345 # 按 pid 查(多进程 App 用这个)
adb shell dumpsys meminfo # 全系统所有进程内存排行
adb shell dumpsys meminfo --checkin com.example.app # CSV 格式输出(适合写脚本采集)
```
详细输出怎么读(判断泄漏/内存优化的核心依据):
```
Pss(KB) Private Dirty Heap Size Heap Alloc
Java Heap: 45123 44800 61440 43210 ← Java 对象堆
Native Heap: 60234 59800 102400 88700 ← C/C++ 内存、图片解码
Graphics: 20000 19800 ← GPU 纹理/缓冲
...
TOTAL PSS: 145678 ← 总占用(持续涨 = 泄漏)
Objects(对象统计):
Views: 1234 View 数量异常大 = 布局泄漏/重复添加
Activities: 8 ⭐ 理论上应 ≤ 当前存活的 Activity 数,
一直涨不降 = Activity 泄漏(经典 bug)
AppContexts: 9 Context 泄漏同理
SQL(数据库):
MEMORY_USED: 2048 数据库内存;heap 一直涨 = Cursor 没 close
```
**泄漏判定黄金法则**:反复进出同一页面 10 次,每次执行 `dumpsys meminfo`,盯 `Activities` 和 `TOTAL PSS` 两行------只涨不降就是泄漏,有实锤再上 Memory Profiler 深挖。
### 7.4 查 CPU 与渲染:cpuinfo / gfxinfo
```bash
# CPU
adb shell dumpsys cpuinfo # 全系统 CPU 占用报表(用户态/内核态占比)
adb shell dumpsys cpuinfo | grep -A 3 com.example.app
adb shell top -m 10 -s 9 # 非 dumpsys 但常用:按 CPU 排序的前 10 进程
# 渲染帧统计(查卡顿)
adb shell dumpsys gfxinfo com.example.app # 渲染统计
adb shell dumpsys gfxinfo com.example.app reset # ⭐ 重置统计后再复现,数据才准
adb shell dumpsys gfxinfo com.example.app framestats # 最近 120 帧逐帧耗时(ns)
```
**gfxinfo 输出怎么读**:
```
Total frames rendered: 320
Janky frames: 45 (14.06%) ← 超过 16.67ms 渲染的帧叫 janky,占比 >10% 肉眼可感卡顿
95th percentile: 24ms ← 95% 的帧能在 24ms 内渲染完
```
`framestats` 的逐帧数据可以自己算出 measure/layout/draw 各阶段耗时,是定位"布局太深还是绘制太重"的依据。
### 7.5 查电量与后台管控:battery / batterystats / deviceidle / alarm / jobscheduler
```bash
# ---- battery:当前电池状态 + 模拟电池(测试神器)----
adb shell dumpsys battery # 电量、电压、温度、充电方式
adb shell dumpsys battery set ac 1 # 模拟插上充电器
adb shell dumpsys battery unplug # 模拟拔电
adb shell dumpsys battery set level 15 # 模拟低电量(测省电模式降级逻辑)
adb shell dumpsys battery reset # ⚠️ 恢复真实状态,改完必做
# ---- batterystats:耗电统计(查"谁在耗电")----
adb shell dumpsys batterystats --reset # 重置统计
# ...正常使用一段时间或跑自动化...
adb shell dumpsys batterystats --charged > battery.txt # 导出自上次充满后的耗电统计
# 在 battery.txt 里搜你的包名,看 Wake lock(后台唤醒锁持有时间)和 Network(后台流量)
# ---- deviceidle:Doze 省电模式 ----
adb shell dumpsys deviceidle # 当前 Doze 状态
adb shell dumpsys deviceidle force-idle # ⭐ 强制进入 Doze(测后台被限流的表现)
adb shell dumpsys deviceidle step # 手动推进 Doze 深睡流程
adb shell dumpsys deviceidle unforce # 退出 Doze
adb shell dumpsys deviceidle whitelist # Doze 白名单(谁不受限)
# ---- alarm / jobscheduler:定时任务 ----
adb shell dumpsys alarm | grep -A 5 com.example.app # App 注册了哪些闹钟、下次触发时间
adb shell dumpsys jobscheduler | grep -B 2 -A 10 com.example.app # JobScheduler 任务
# ---- power:电源状态 ----
adb shell dumpsys power | grep -iE "wakefulness|wake_lock" # 亮灭屏、唤醒锁统计
```
**实战场景**:后台推送收不到、定时任务不跑?先 `dumpsys battery unplug && dumpsys deviceidle force-idle` 把设备推进 Doze 再观察------90% 的"后台收不到推送"是 Doze 限制,不是 bug。
### 7.6 查网络:connectivity / netstats / netpolicy
```bash
adb shell dumpsys connectivity # 网络连接状态(默认网络是谁、验证状态)
adb shell dumpsys netstats detail # 流量统计(查流量异常)
adb shell dumpsys netpolicy # 后台网络限制状态(流量省电策略)
```
网络连通性测试(ping/ip 等)。
### 7.7 其他高频服务速查表
| 服务 | 命令 | 什么时候用 |
| :--- | :--- | :--- |
| package | `dumpsys package com.example.app` | 查版本、签名、权限、安装来源 |
| notification | `dumpsys notification --noredact` | 通知没显示?看通知队列和拦截状态 |
| dropbox | `dumpsys dropbox --print data_app_crash` | ⭐ 历史崩溃存档(没开 logcat 也能事后翻案) |
| procstats | `dumpsys procstats com.example.app` | 长时段内存/CPU 历史统计(评估后台占用) |
| usagestats | `dumpsys usagestats` | App 使用时长统计 |
| thermalservice | `dumpsys thermalservice` | 温度状态(降频/降亮度排查) |
| diskstats | `dumpsys diskstats` | 磁盘空间 |
| mount | `dumpsys mount` | 分区挂载状态(查 remount 问题) |
| media.session | `dumpsys media_session` | 音视频会话(谁在播、锁屏控件归谁) |
| audio | `dumpsys media.audio_flinger` | 音频焦点("为什么我的 App 没声音") |
| input_method | `dumpsys input_method` | 输入法状态(软键盘弹不出来) |
| vibrator | `dumpsys vibrator_manager` | 震动效果(测试震动反馈) |
| location | `dumpsys location` | 定位状态、有哪些 App 在请求定位 |
| account | `dumpsys account` | 系统账户 |
| backup | `dumpsys backup` | 备份服务状态 |
其中 **`dumpsys dropbox`** 最容易被忽略:系统把每次 App 崩溃都存档了,哪怕你没开着 logcat 也能事后翻案:
```bash
adb shell dumpsys dropbox --print data_app_crash | head -100 # 最近一次 Java 崩溃的完整堆栈
adb shell dumpsys dropbox --print data_app_anr | head -100 # 历史 ANR 记录
```
### 7.8 dumpsys 实战心法
1. **不知道查哪个服务?** `dumpsys -l` 找名字,服务名基本见名知义;
2. **查 App 相关问题,命令后面直接跟包名**(activity/services/alarm/jobscheduler/package/meminfo/procstats 都支持),避免在几千行里大海捞针;
3. **复现类测量先 reset**:`gfxinfo ... reset`、`batterystats --reset`,让统计数据从零开始;
4. **改完 battery / deviceidle 的模拟状态,记得 reset/unforce 恢复**,否则设备一直处于假状态;
5. **配合 logcat 交叉验证**:dumpsys 告诉你"现在是什么状态",logcat 告诉你"状态怎么变成这样的",两者对时间轴就能还原整个事件链。
### 7.9 配套的通用 Linux 信息命令
查硬件/内核这类"非服务"信息,用 cat /proc 即可:
```bash
adb shell cat /proc/cpuinfo # CPU
adb shell cat /proc/meminfo # 总内存
adb shell cat /proc/version # 内核版本
adb shell df -h # 磁盘空间
adb shell free -m # 内存概况
adb shell uptime # 开机时长、负载
```
---
## 八、模拟操作
### 8.1 触摸与按键
```bash
adb shell input tap 500 800 # 点击坐标 (x,y)
adb shell input swipe 300 1000 300 300 # 滑动(快速)
adb shell input swipe 300 1000 300 300 500 # 滑动,持续 500ms
adb shell input swipe 300 800 300 800 2000 # 长按(同点按住 2 秒)
adb shell input keyevent 4 # 返回键
adb shell input keyevent 3 # Home 键
adb shell input keyevent 26 # 电源键
adb shell input text "hello" # 输入文字(焦点在输入框时)
adb shell input text "hello%sworld" # 空格用 %s 代替
```
常用 keyevent 码速查:`3` Home、`4` 返回、`5` 拨号、`6` 挂断、`24` 音量+、`25` 音量-、`26` 电源、`82` 菜单、`127` 暂停媒体、`164` 静音。
**怎么知道该点哪个坐标?** 开发者选项 → 指针位置(打开后屏幕显示坐标),或者:
```bash
adb shell uiautomator dump /sdcard/ui.xml # 导出当前界面控件树
adb pull /sdcard/ui.xml && open ui.xml # 找到目标控件的 bounds="[x1,y1][x2,y2]"
```
### 8.2 截屏 / 录屏
```bash
# 截屏
adb exec-out screencap -p > screen.png
# 或两步走:
adb shell screencap /sdcard/s.png && adb pull /sdcard/s.png
# 录屏(默认 3 分钟上限,Ctrl+C 停止)
adb shell screenrecord /sdcard/demo.mp4
adb shell screenrecord --time-limit 30 --bit-rate 8000000 /sdcard/demo.mp4 # 30 秒、高码率
adb pull /sdcard/demo.mp4
```
录屏报 bug 复现视频 + logcat 文本,是给同事/社区提 issue 的黄金组合。
### 8.3 模拟系统状态
```bash
# 电池模拟走 dumpsys(详见 7.5):
adb shell dumpsys battery set level 15 && adb shell dumpsys battery unplug
# 修改 DPI / 分辨率(适配测试神器!)
adb shell wm size 1080x1920 # 改分辨率
adb shell wm density 320 # 改 DPI
adb shell wm size reset && adb shell wm density reset # 恢复
```
**`wm size/density` 是做屏幕适配测试的利器**:不用真机矩阵,一条命令把旗舰机变成小屏机。
---
## 九、性能定位 SOP
本章只讲**排查流程**(先做什么后做什么),命令细节和输出解读都可以参考上面的细节。
### 9.1 卡顿 / 掉帧
```bash
adb shell dumpsys gfxinfo com.example.app reset # 1. 重置统计(输出解读见 7.4)
# 2. 手机上操作,复现卡顿
adb shell dumpsys gfxinfo com.example.app # 3. 看 Janky frames 占比与 95th percentile
adb shell dumpsys gfxinfo com.example.app framestats # 4. 逐帧数据定位慢在哪一阶段
```
进阶:抓完整渲染 trace(用 ui.perfetto.dev 打开):
```bash
adb shell perfetto -o /data/misc/perfetto-traces/trace.pftrace -t 15s sched freq gfx view am wm
adb pull /data/misc/perfetto-traces/trace.pftrace
```
### 9.2 内存泄漏嫌疑
```bash
adb shell dumpsys meminfo com.example.app | grep TOTAL # 1. 记录基线(输出解读见 7.3)
# 2. 反复进出同一页面 10 次,每轮执行上面命令
# 3. TOTAL PSS / Activities 只涨不降 → 泄漏,转 Memory Profiler 或 LeakCanary 深挖
adb shell ps -eo PID,RSS,NAME --sort=-rss | head -20 # 顺带:全机内存排行
```
### 9.3 CPU 占用过高
```bash
adb shell top -m 10 -s 9 # 1. 找出最耗 CPU 的进程(确认是不是你的)
adb shell dumpsys cpuinfo | grep -A 3 com.example.app # 2. 看你的 App 用户态/内核态占比(见 7.4)
adb shell pidof com.example.app # 3. 拿到 pid 后可抓 perfetto/simpleperf 深挖热点函数
```
### 9.4 启动时间
```bash
adb shell am force-stop com.example.app # 1. 杀掉保证冷启动
adb shell am start -W -n com.example.app/.MainActivity
```
输出:
```
Status: ok
TotalTime: 456 ← 从启动到第一帧的总耗时(ms)
WaitTime: 512 ← 包括 AMS 等待的时间
```
跑 5 次取平均才是可信数据。
### 9.5 耗电
```bash
adb shell dumpsys batterystats --reset # 1. 重置统计(见 7.5)
# 2. 正常使用一段时间或跑自动化
adb shell dumpsys batterystats --charged > battery.txt # 3. 导出,搜包名看 Wake lock 和 Network
```
后台耗电大户基本就是唤醒锁(Wake lock)和后台流量(Network)两项。
---
## 十、网络调试
### 10.1 连通性与状态
```bash
adb shell ping -c 4 baidu.com # 手机上 ping
adb shell ifconfig wlan0 # Wi-Fi IP 地址
adb shell ip route # 路由表
adb shell netstat -tlnp # 监听端口(部分版本需 root)
adb shell dumpsys connectivity # 连接状态详情(见 7.6)
adb shell svc wifi enable / disable # 开关 Wi-Fi
adb shell svc data enable / disable # 开关移动数据
```
### 10.2 端口转发:电脑和手机互访
```bash
# 电脑访问手机上的服务(如手机上跑了个调试服务器在 8080)
adb forward tcp:8080 tcp:8080
# 然后浏览器打开 http://localhost:8080 就是手机的 8080
# 手机访问电脑上的服务(手机连不上你电脑的 mock 服务器?用它!)
adb reverse tcp:3000 tcp:3000
# 手机 App 里访问 http://localhost:3000,实际打到电脑的 3000
# 转发到手机内部的 Unix socket(调 Chrome 远程调试 WebView)
adb forward tcp:9222 localabstract:chrome_devtools_remote
# 电脑 Chrome 打开 chrome://inspect 即可调试手机 WebView
```
`adb reverse` 是真机调试的保命符------手机没公网 IP、和电脑不在同一段网,都拦不住它。
---
## 十一、实战剧本:常见问题排查
### 剧本 1:App 闪退(Crash)
```bash
# 1. 清日志,准备复现
adb logcat -c
# 2. (在手机上操作复现闪退)
adb logcat -d -b crash -v threadtime
```
看输出里的 `FATAL EXCEPTION` 段:异常类型(`NullPointerException` 等)+ 发生的类名行号,直接定位代码。如果闪退但 crash 缓冲区没东西,可能是 native 崩溃:
```bash
adb logcat -d | grep -E "SIGSEGV|SIGABRT|tombstone|Fatal signal"
```
**没抓到现场?崩溃记录有存档**(详见 7.7 的 dropbox):
```bash
adb shell dumpsys dropbox --print data_app_crash | head -100 # 系统存档的历史崩溃堆栈
```
### 剧本 2:App 无响应(ANR)
```bash
adb logcat -d | grep -i "ANR in" # 1. 确认发生了 ANR 及哪个进程
adb logcat -d -b events | grep am_anr # 2. 事件流里看 ANR 记录
adb shell ls /data/anr/ # 3. root 机可看完整线程堆栈
adb shell cat /data/anr/anr_* | head -100 # 主线程堆栈第一段就是凶手
```
非 root 机拿不到 `/data/anr/`,用 `adb bugreport anr.zip`,解压后在 `FS/data/anr/` 里有同样的文件;历史 ANR 也可查 `dumpsys dropbox --print data_app_anr`。
### 剧本 3:App 越用越卡(内存泄漏嫌疑)
按 9.2 的 SOP 执行:`dumpsys meminfo` 反复采样 → 盯 TOTAL PSS 和 Activities → 只涨不降即泄漏。
### 剧本 4:页面白屏 / 界面卡住
```bash
adb shell dumpsys activity top | grep ACTIVITY # 当前 Activity 是谁?(见 7.2)
adb shell dumpsys window | grep mCurrentFocus # 焦点在哪个窗口?
adb exec-out screencap -p > now.png # 截屏留证
adb shell uiautomator dump /sdcard/ui.xml # 界面树里有没有内容?(白屏还是数据空?)
adb pull /sdcard/ui.xml
```
### 剧本 5:App 装不上
```bash
adb install app.apk
# 看最后的 INSTALL_FAILED_XXX 是什么,对照第十二节速查表
adb shell dumpsys package com.example.app | grep version # 是不是版本号冲突
```
### 剧本 6:怀疑 App 在后台偷偷活动
```bash
adb shell dumpsys activity processes | grep -A 5 com.example.app # 进程还活着吗?(见 7.2)
adb shell dumpsys alarm | grep -A 3 com.example.app # 注册了什么闹钟?(见 7.5)
adb shell dumpsys jobscheduler | grep -B 2 -A 8 com.example.app # 有没有定时任务?
adb logcat -d -b events | grep "am_wake_lock" # 谁在拿唤醒锁?
```
### 剧本 7:系统起不来 / 反复重启
```bash
adb wait-for-device logcat -d -b all > boot.log # 第一时间抢救日志!
adb logcat -d | grep -iE "fatal|crash|zygote" # 看 zygote 有没有挂
adb reboot recovery # 进 recovery 清数据(会丢用户数据)
adb reboot bootloader # 进 fastboot 重刷(最后手段)
```
**血泪经验**:刷机/改系统后 bootloop,第一件事不是重启,而是 `wait-for-device` 抢日志------设备循环重启时 adbd 有一小段存活窗口,抓到日志就抓住了根因。
### 剧本 8:把完整证据包发给别人
```bash
adb exec-out screencap -p > screenshot.png
adb shell screenrecord --time-limit 20 /sdcard/repro.mp4 # 边录边复现
adb bugreport evidence.zip # 全量体检包(所有 dumpsys/anr/日志全在)
adb pull /sdcard/repro.mp4
```
一个 `evidence.zip` + 一段录屏 + 一份 logcat,任何人都够定位 80% 的问题。
---
## 十二、报错速查表
| 报错 | 原因 | 解法 |
| :--- | :--- | :--- |
| `unauthorized` | 手机没点授权弹窗 | 看手机屏幕点允许;不行就撤销授权重连(开发者选项 → 撤销 USB 调试授权) |
| `offline` | 握手未完成 | 换线/换口;`adb kill-server && adb devices` |
| `adb server version doesn't match` | 装了两个版本的 adb | `which -a adb` 找出重复的,删一个 |
| `more than one device` | 多设备 | 加 `-s 序列号` 或设 `ANDROID_SERIAL` |
| `INSTALL_FAILED_ALREADY_EXISTS` | 已装同包名 | 加 `-r` 覆盖装 |
| `INSTALL_FAILED_SIGNATURE_CONFLICT` | 签名不一致 | 先 `adb uninstall` 再装 |
| `INSTALL_FAILED_UPDATE_INCOMPATIBLE` | 系统里有残留/版本冲突 | `adb uninstall` 后重装;还不行 `pm clear` |
| `INSTALL_FAILED_NO_MATCHING_ABIS` | apk 不含设备 CPU 架构 | 换含 arm64-v8a 的包 |
| `INSTALL_PARSE_FAILED_NO_CERTIFICATES` | apk 没签名 | 用 apksigner 签名后再装 |
| `Read-only file system` | push 到了系统分区 | 先 root + remount,或推到 `/data/local/tmp` |
| `device offline` during install | 安装过程中设备掉线 | 换好线,关掉省电 USB 休眠 |
| `adb: no devices/emulators found` | 没设备 | 查 USB 调试开关、驱动、`lsusb` 看设备有没有枚举到 |
| `protocol fault (no status)` | server 与 adbd 通信崩了 | `adb kill-server` 重来;频繁出现换线 |
| `cmd: Failure calling service` | 服务不存在或被裁剪 | `dumpsys -l` 确认服务名;定制 ROM 可能删了该服务 |
---
## 十三、效率技巧
### 13.1 组合命令
```bash
# 一行完成:截屏并拉回电脑
adb exec-out screencap -p > $(date +%H%M%S).png
# 一行完成:杀 App 重启(改代码后的快速验证)
adb shell am force-stop com.example.app && adb shell am start -n com.example.app/.MainActivity
# 一行完成:等设备 + 确认开机 + 提示
adb wait-for-device shell 'while [ "$(getprop sys.boot_completed)" != "1" ]; do sleep 1; done; echo BOOT_OK'
```
### 13.2 常用自带工具箱(adb shell 里的 Linux 命令)
```bash
adb shell ls -l /sdcard/ # 列目录
adb shell cat file.txt # 看文件
adb shell grep -r "keyword" /data/local/tmp/ # 搜内容
adb shell stat file.txt # 看文件属性
adb shell toybox # 查看全部可用工具列表(Android 自带 toybox)
```
adb shell 里可用的是精简版 Linux 工具集(toybox),常用命令基本都有:`ls cp mv rm mkdir ps top netstat ifconfig df du free kill` 等。
后面有了再补充。。。