0%

Android Debug Bridge Tips

图形化界面太低效,还存在DDMS连接不稳定的隐患,尽量通过命令行方式高效解决

原文详细请参考:ADB Usage Complete

常用命令的简单描述

cat 显示文件内容
cd 切换目录
chmod 改变文件的存取模式/访问权限
df 查看磁盘空间使用情况
grep 过滤输出
kill 杀死指定 PID 的进程
ls 列举目录内容
mount 挂载目录的查看和管理
mv 移动或重命名文件
ps 查看正在运行的进程
rm 删除文件
top 查看进程的资源占用情况

USB 连接

通过 USB 连接来正常使用 adb 需要保证几点:

硬件状态正常。

包括 Android 设备处于正常开机状态,USB 连接线和各种接口完好。

Android 设备的开发者选项和 USB 调试模式已开启。

可以到「设置」-「开发者选项」-「Android 调试」查看。

如果在设置里找不到开发者选项,那需要通过一个彩蛋来让它显示出来:在「设置」-「关于手机」连续点击「版本号」7 次。

设备驱动状态正常。

这一点貌似在 Linux 和 Mac OS X 下不用操心,在 Windows 下有可能遇到需要安装驱动的情况,确认这一点可以右键「计算机」-「属性」,到「设备管理器」里查看相关设备上是否有黄色感叹号或问号,如果没有就说明驱动状态已经好了。否则可以下载一个手机助手类程序来安装驱动先。

通过 USB 线连接好电脑和设备后确认状态。

1
adb devices

如果能看到

xxxxxx device
说明连接成功。

以 root 权限运行 adbd

adb 的运行原理是 PC 端的 adb server 与手机端的守护进程 adbd 建立连接,然后 PC 端的 adb client 通过 adb server 转发命令,adbd 接收命令后解析运行。

所以如果 adbd 以普通权限执行,有些需要 root 权限才能执行的命令无法直接用 adb xxx 执行。这时可以 adb shell 然后 su 后执行命令,也可以让 adbd 以 root 权限执行,这个就能随意执行高权限命令了。

命令:

1
adb root

现在再运行 adb shell,看看命令行提示符是不是变成 # 了?

有些手机 root 后也无法通过 adb root 命令让 adbd 以 root 权限执行,比如三星的部分机型,会提示 adbd cannot run as root in production builds,此时可以先安装 adbd Insecure,然后 adb root 试试。

相应地,如果要恢复 adbd 为非 root 权限的话,可以使用 adb unroot 命令。

安装 APK

命令格式:

adb install [-lrtsdg] <path_to_apk>
参数:

adb install 后面可以跟一些可选参数来控制安装 APK 的行为,可用参数及含义如下:

参数 含义
-l 将应用安装到保护目录 /mnt/asec
-r 允许覆盖安装
-t 允许安装 AndroidManifest.xml 里 application 指定 android:testOnly=”true” 的应用
-s 将应用安装到 sdcard
-d 允许降级覆盖安装
-g 授予所有运行时权限
运行命令后如果见到类似如下输出(状态为 Success)代表安装成功:

1
2
3
[100%] /data/local/tmp/1.apk
pkg: /data/local/tmp/1.apk
Success

参考:PackageManager.java

adb install 内部原理简介

adb install 实际是分三步完成:

push apk 文件到 /data/local/tmp。

调用 pm install 安装。

删除 /data/local/tmp 下的对应 apk 文件。

所以,必要的时候也可以根据这个步骤,手动分步执行安装过程。

卸载应用

命令:

1
adb uninstall [-k] <packagename>

表示应用的包名,-k 参数可选,表示卸载应用但保留数据和缓存目录。

命令示例:

1
adb uninstall com.qihoo360.mobilesafe

表示卸载 360 手机卫士。

复制设备里的文件到电脑

命令:

1
adb pull <设备里的文件路径> [电脑上的目录]

其中 电脑上的目录 参数可以省略,默认复制到当前目录。

例:

1
adb pull /sdcard/sr.mp4 ~/tmp/

小技巧:设备上的文件路径可能需要 root 权限才能访问,如果你的设备已经 root 过,可以先使用 adb shell 和 su 命令在 adb shell 里获取 root 权限后,先 cp /path/on/device /sdcard/filename 将文件复制到 sdcard,然后 adb pull /sdcard/filename /path/on/pc。

复制电脑里的文件到设备

命令:

1
adb push <电脑上的文件路径> <设备里的目录>

例:

1
adb push ~/sr.mp4 /sdcard/

小技巧:设备上的文件路径普通权限可能无法直接写入,如果你的设备已经 root 过,可以先 adb push /path/on/pc /sdcard/filename,然后 adb shell 和 su 在 adb shell 里获取 root 权限后,cp /sdcard/filename /path/on/device。

实战派:

1
2
adb push AdbTest\. /sdcard/Download
/sdcard/Download/./: 4 files pushed. 0 files skipped. 0.1 MB/s (3726 bytes in 0.057s)

关键点:使用 “.”是可以一次性 push一个文件目录的。
如果AdbTest有子目录会如何?如果子目录中有文件,则会将子目录和其中的文件一起推送过去,如果子目录是空的,则会跳过

1
adb push comment12031715.txt commenttest.txt userLibrary.dic /sdcard/Download

adb 一次性推送多个文件,如果目标文件夹中存在同名文件,会如何?默认是直接覆盖的,

1
2
3
4
5
C:\Users\user\Desktop>adb push smile.lrc /storage/sdcard/Download
smile.lrc: 1 file pushed. 0.0 MB/s (1061 bytes in 0.100s)

C:\Users\user\Desktop>adb push smile.jpg /storage/sdcard/Download
smile.jpg: 1 file pushed. 0.6 MB/s (51419 bytes in 0.088s)

遇到读写权限问题的解决办法

在shell命令行中输入:adb root
切换到root用户
然后执行如下命令:(注意 /表示的是根目录,因此这个不仅仅是对sdcard,其他文件也可以读写。rw表示读写权限,mount重挂载文件系统)
adb shell mount -o remount rw / , 修改系统读写权限

注意系统文件在push后需要修改权限,以framework-res.apk为例,输入:adb shell chmod 644 /system/framework/framework-res.apk回车,然后设备重启下就替换完成了。

查看日志

Android 系统的日志分为两部分,底层的 Linux 内核日志输出到 /proc/kmsg,Android 的日志输出到 /dev/log。

Android 日志

命令格式:

1
[adb] logcat [<option>] ... [<filter-spec>] ...

常用用法列举如下:

按级别过滤日志

Android 的日志分为如下几个优先级(priority):

V —— Verbose(最低,输出得最多)
D —— Debug
I —— Info
W —— Warning
E —— Error
F —— Fatal
S —— Silent(最高,啥也不输出)
按某级别过滤日志则会将该级别及以上的日志输出。

比如,命令:

1
adb logcat *:W

会将 Warning、Error、Fatal 和 Silent 日志输出。

(注:在 macOS 下需要给 :W 这样以 作为 tag 的参数加双引号,如 adb logcat “:W”,不然会报错 no matches found: :W。)

按 tag 和级别过滤日志

可以由多个 [:priority] 组成。

比如,命令:

1
adb logcat ActivityManager:I MyApp:D *:S

表示输出 tag ActivityManager 的 Info 以上级别日志,输出 tag MyApp 的 Debug 以上级别日志,及其它 tag 的 Silent 级别日志(即屏蔽其它 tag 日志)。

清空日志

1
adb logcat -c

内核日志

命令:

1
adb shell dmesg

输出示例:

<6>[14201.684016] PM: noirq resume of devices complete after 0.982 msecs

<6>[14201.685525] PM: early resume of devices complete after 0.838 msecs

<6>[14201.753642] PM: resume of devices complete after 68.106 msecs

<4>[14201.755954] Restarting tasks … done.

<6>[14201.771229] PM: suspend exit 2016-08-28 13:31:32.679217193 UTC

<6>[14201.872373] PM: suspend entry 2016-08-28 13:31:32.780363596 UTC

<6>[14201.872498] PM: Syncing filesystems … done.
中括号里的 [14201.684016] 代表内核开始启动后的时间,单位为秒。

通过内核日志我们可以做一些事情,比如衡量内核启动时间,在系统启动完毕后的内核日志里找到 Freeing init memory 那一行前面的时间就是。

屏幕截图

命令:

1
adb shell screencap -p /sdcard/sc.png

然后将 png 文件导出到电脑:

1
adb pull /sdcard/sc.png

可以使用 adb shell screencap -h 查看 screencap 命令的帮助信息,下面是两个有意义的参数及含义:

参数 含义
-p 指定保存文件为 png 格式
-d display-id 指定截图的显示屏编号(有多显示屏的情况下)
实测如果指定文件名以 .png 结尾时可以省略 -p 参数;否则需要使用 -p 参数。如果不指定文件名,截图文件的内容将直接输出到 stdout。

直接一行命令截图并保存到电脑的方法:

Linux 和 Windows

1
adb shell screencap -p | sed "s/\r$//" > sc.png

Mac OS X

1
adb shell screencap -p | gsed "s/\r$//" > sc.png

这个方法需要用到 gnu sed 命令,在 Linux 下直接就有,在 Windows 下 Git 安装目录的 bin 文件夹下也有。如果确实找不到该命令,可以下载 sed for Windows 并将 sed.exe 所在文件夹添加到 PATH 环境变量里。

而在 Mac 下使用系统自带的 sed 命令会报错:

1
sed: RE error: illegal byte sequence

需要安装 gnu-sed,然后使用 gsed 命令:

1
brew install gnu-sed

录制屏幕

录制屏幕以 mp4 格式保存到 /sdcard:

1
adb shell screenrecord /sdcard/filename.mp4

需要停止时按 Ctrl-C,默认录制时间和最长录制时间都是 180 秒。

如果需要导出到电脑:

1
adb pull /sdcard/filename.mp4

可以使用 adb shell screenrecord –help 查看 screenrecord 命令的帮助信息,下面是常见参数及含义:

参数 含义
–size WIDTHxHEIGHT 视频的尺寸,比如 1280x720,默认是屏幕分辨率。
–bit-rate RATE 视频的比特率,默认是 4Mbps。
–time-limit TIME 录制时长,单位秒。
–verbose 输出更多信息。

重新挂载 system 分区为可写

注:需要 root 权限。

/system 分区默认挂载为只读,但有些操作比如给 Android 系统添加命令、删除自带应用等需要对 /system 进行写操作,所以需要重新挂载它为可读写。

步骤:

进入 shell 并切换到 root 用户权限。

命令:

1
2
adb shell
su

查看当前分区挂载情况。

命令:

1
mount

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
rootfs / rootfs ro,relatime 0 0
tmpfs /dev tmpfs rw,seclabel,nosuid,relatime,mode=755 0 0
devpts /dev/pts devpts rw,seclabel,relatime,mode=600 0 0
proc /proc proc rw,relatime 0 0
sysfs /sys sysfs rw,seclabel,relatime 0 0
selinuxfs /sys/fs/selinux selinuxfs rw,relatime 0 0
debugfs /sys/kernel/debug debugfs rw,relatime 0 0
none /var tmpfs rw,seclabel,relatime,mode=770,gid=1000 0 0
none /acct cgroup rw,relatime,cpuacct 0 0
none /sys/fs/cgroup tmpfs rw,seclabel,relatime,mode=750,gid=1000 0 0
none /sys/fs/cgroup/memory cgroup rw,relatime,memory 0 0
tmpfs /mnt/asec tmpfs rw,seclabel,relatime,mode=755,gid=1000 0 0
tmpfs /mnt/obb tmpfs rw,seclabel,relatime,mode=755,gid=1000 0 0
none /dev/memcg cgroup rw,relatime,memory 0 0
none /dev/cpuctl cgroup rw,relatime,cpu 0 0
none /sys/fs/cgroup tmpfs rw,seclabel,relatime,mode=750,gid=1000 0 0
none /sys/fs/cgroup/memory cgroup rw,relatime,memory 0 0
none /sys/fs/cgroup/freezer cgroup rw,relatime,freezer 0 0
/dev/block/platform/msm_sdcc.1/by-name/system /system ext4 ro,seclabel,relatime,data=ordered 0 0
/dev/block/platform/msm_sdcc.1/by-name/userdata /data ext4 rw,seclabel,nosuid,nodev,relatime,noauto_da_alloc,data=ordered 0 0
/dev/block/platform/msm_sdcc.1/by-name/cache /cache ext4 rw,seclabel,nosuid,nodev,relatime,data=ordered 0 0
/dev/block/platform/msm_sdcc.1/by-name/persist /persist ext4 rw,seclabel,nosuid,nodev,relatime,data=ordered 0 0
/dev/block/platform/msm_sdcc.1/by-name/modem /firmware vfat ro,context=u:object_r:firmware_file:s0,relatime,uid=1000,gid=1000,fmask=0337,dmask=0227,codepage=cp437,iocharset=iso8859-1,shortname=lower,errors=remount-ro 0 0
/dev/fuse /mnt/shell/emulated fuse rw,nosuid,nodev,relatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0
/dev/fuse /mnt/shell/emulated/0 fuse rw,nosuid,nodev,relatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0

找到其中我们关注的带 /system 的那一行:

1
/dev/block/platform/msm_sdcc.1/by-name/system /system ext4 ro,seclabel,relatime,data=ordered 0 0

重新挂载。

命令:

1
mount -o remount,rw -t yaffs2 /dev/block/platform/msm_sdcc.1/by-name/system /system

这里的 /dev/block/platform/msm_sdcc.1/by-name/system 就是我们从上一步的输出里得到的文件路径。

如果输出没有提示错误的话,操作就成功了,可以对 /system 下的文件为所欲为了。

重启手机

命令:

1
adb reboot

检测设备是否已 root

命令:

1
2
adb shell
su

此时命令行提示符是 $ 则表示没有 root 权限,是 # 则表示已 root。

使用 Monkey 进行压力测试

Monkey 可以生成伪随机用户事件来模拟单击、触摸、手势等操作,可以对正在开发中的程序进行随机压力测试。

简单用法:

1
adb shell monkey -p <packagename> -v 500

表示向 指定的应用程序发送 500 个伪随机事件。

Monkey 的详细用法参考 官方文档

查看进程

命令:

1
adb shell ps

输出示例:

1
2
3
4
5
6
7
8
USER     PID   PPID  VSIZE  RSS     WCHAN    PC        NAME
root 1 0 8904 788 ffffffff 00000000 S /init
root 2 0 0 0 ffffffff 00000000 S kthreadd
...
u0_a71 7779 5926 1538748 48896 ffffffff 00000000 S com.sohu.inputmethod.sogou:classic
u0_a58 7963 5926 1561916 59568 ffffffff 00000000 S org.mazhuang.boottimemeasure
...
shell 8750 217 10640 740 00000000 b6f28340 R ps

各列含义:

列名 含义
USER 所属用户
PID 进程 ID
PPID 父进程 ID
NAME 进程名

查看实时资源占用情况

命令:

1
adb shell top

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
User 0%, System 6%, IOW 0%, IRQ 0%
User 3 + Nice 0 + Sys 21 + Idle 280 + IOW 0 + IRQ 0 + SIRQ 3 = 307

PID PR CPU% S #THR VSS RSS PCY UID Name
8763 0 3% R 1 10640K 1064K fg shell top
131 0 3% S 1 0K 0K fg root dhd_dpc
6144 0 0% S 115 1682004K 115916K fg system system_server
132 0 0% S 1 0K 0K fg root dhd_rxf
1731 0 0% S 6 20288K 788K fg root /system/bin/mpdecision
217 0 0% S 6 18008K 356K fg shell /sbin/adbd
...
7779 2 0% S 19 1538748K 48896K bg u0_a71 com.sohu.inputmethod.sogou:classic
7963 0 0% S 18 1561916K 59568K fg u0_a58 org.mazhuang.boottimemeasure
...

各列含义:

列名 含义
PID 进程 ID
PR 优先级
CPU% 当前瞬间占用 CPU 百分比
S 进程状态(R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程)

#THR 线程数
VSS Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PCY 调度策略优先级,SP_BACKGROUND/SPFOREGROUND
UID 进程所有者的用户 ID
NAME 进程名
top 命令还支持一些命令行参数,详细用法如下:

Usage: top [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]
-m num 最多显示多少个进程
-n num 刷新多少次后退出
-d num 刷新时间间隔(单位秒,默认值 5)
-s col 按某列排序(可用 col 值:cpu, vss, rss, thr)
-t 显示线程信息
-h 显示帮助文档

欢迎关注我的其它发布渠道