Android Debug Bridge
Android调试桥(ADB)是一种多用途的命令行工具。通过它我们可以和模拟器或者设备通信。ADB是一个客户端-服务器程序,包括三个组成部分:
- 客户端(Client),运行在你用于程序开发的电脑上。你可以通过shell端使用adb命令启动客户端。其他Android工具,例如ADT插件和DDMS同样可以产生adb客户端。
- 服务器(Server),以后台进程的形式运行在你用于程序开发的电脑上。该服务器负责管理客户端和运行于模拟器或设备上的adb守护进程(daemon)之间的通信。
- 守护进程(Daemon),以后台进程的形式运行在模拟器或者设备上。
你可以在sdk/platform-tools下面找到adb工具。
当你启动一个adb客户端,客户端首先检测是否已有一个adb服务器进程正在运行。如果没有,则启动服务器进程。当服务器运行,adb服务器就会绑定本地的TCP端口5037并监听adb客户端发来的命令——所有的adb客户端都是通过TCP端口5037与adb服务器进行通信。
接下来,服务器将所有运行中的模拟器或设备实例建立连接。它通过扫描所有5555到5585范围内的奇数端口来定位所有的模拟器或设备。一旦服务器找到了adb守护进程,它将建立一个到该端口的连接。请注意每个模拟器或设备实例都会取得两个连续的端口——偶数端口响应控制台连接,奇数端口响应adb连接。例如:
Emulator 1, console: 5554
Emulator 1, adb: 5555
Emulator 2, console: 5556
Emulator 2, adb: 5557
如上所示,通过5555端口连接adb的模拟器实例和通过5554端口连接控制台的实例是相同的。
一旦服务器与所有模拟器实例建立连接,就可以使用adb命令来访问该实例。因为服务器管理模拟器或设备实例的连接,而且处理来自多个adb客户端的命令,你可以通过任何客户端(或脚本)来控制任何模拟器或设备实例。
你可以在PC的命令行或脚本上发布Android命令,使用方法:
语法
adb [-d|-e|-s <serialNumber>] <command>
如果仅有一个模拟器运行或一个设备连接,adb命令默认发送给这个设备。如果有多个模拟器运行或多个设备连接,你就需要使用-d, -e, -s选项来指定接收命令的目标设备。
ADB常用命令
- adb start-server
- adb kill-server
- adb shell
- adb devices
- adb connect <serialNumber>
- adb pull -s <serialNumber> <remote> <local>
- adb push -s <serialNumber> <local> <remote>
实例演练
上面的学习总结都是铺垫,或者说都是废话,因为在其他技术博客也能看到相关的介绍。接下来才是文章的重点:"adb server is out of date. killing..."
这里我忍不住吐槽一下,网上关于"adb server is out of date. killing..."的介绍都是关于神马“豌豆荚”之类的,没有一篇文章从深层原理发出,解释为什么会出现这种情况。吐槽完毕。。。
为了解决公司里多客户端通过tftp公用adb server造成相互“杀死”对方的情况,我进行了如下测试:
场景:两个DMP Board,三个tftp用户。
1. 查看A,B,C三个用户的adb路径
macan@BG2BLT06:~$ which adb/home/macan/Android_Eclipse/adt-bundle-linux-x86_64-20130219/sdk/platform-tools/adb
[~/macan/Honeycomb]which adb /home/chenwei/macan/Honeycomb/out/host/linux-x86/bin/adb
cwen@BG2BLT06:~$ which adb/home/cwen/android/android-sdk-linux_86/platform-tools/adb
细心的读者可能会发现,三个用户的adb路径都不同啊,怎么说公用一个adb server呢?上面文章讲过:“当你启动一个adb客户端,客户端首先检测是否已有一个adb服务器进程正在运行。如果没有,则启动服务器进程。”为了更好的理解,第二步让我们看看进程情况。
2. 用户B,C启动adb server
[~/macan/Honeycomb]adb start-server* daemon not running. starting it now on port 5037 ** daemon started successfully *
cwen@BG2BLT06:~$ adb start-server
A,B,C用户分别查看进程情况
macan@BG2BLT06:~$ ps aux | grep adbchenwei 14873 0.0 0.0 19988 916 pts/10 Sl 13:38 0:00 adb fork-server servermacan 14891 0.0 0.0 8956 876 pts/12 R+ 13:39 0:00 grep --color=auto adb
[~/macan/Honeycomb]ps axu | grep adbchenwei 14873 0.0 0.0 19988 916 pts/10 Sl 13:38 0:00 adb fork-server serverchenwei 14918 0.0 0.0 8956 876 pts/10 S+ 13:40 0:00 grep --color=auto adb
cwen@BG2BLT06:~$ ps aux | grep adbchenwei 14873 0.0 0.0 19988 916 pts/10 Sl 13:38 0:00 adb fork-server servercwen 14915 0.0 0.0 8952 876 pts/11 R+ 13:40 0:00 grep --color=auto adb
到了这里大家应该就明白了“公用adb server”这句话的意思了。不过,我们还是没有发现所谓的:"adb server is out of date. killing..."
3. 用户A启动adb server(关键)
在B已经启动adb server的情况下,我们发现C执行"adb start-server"没有反应,因为公用B的adb server。那么当A执行“adb start-server”呢?
macan@BG2BLT06:~$ adb start-serveradb server is out of date. killing...* daemon started successfully *
悲剧发生了。。。我们盼望已久的"adb server is out of date. killing..."出现了!这是为啥捏???
先别急,一步步分析。
4. 再次看看进程
macan@BG2BLT06:~$ ps aux |grep adbmacan 15062 0.0 0.0 20944 1404 pts/12 Sl 13:49 0:00 adb fork-server servermacan 15107 0.0 0.0 8956 872 pts/12 R+ 13:52 0:00 grep --color=auto adb
很明显,之前由B的Client fork出的adb server已经被kill,现在的adb server是由A的Client fork出的。。。
这个时候,我们的线索只有一个"adb server is out of date. killing..."!!!
一切都是浮云,源码才是王道!!!
/system/core/adb
通过定位找到了"adb server is out of date. killing..."所在位置
//adb_client.cif(version != ADB_SERVER_VERSION) { printf("*Yagami* version: %d ADB_SERVER_VERSION: %d\n" ,version,ADB_SERVER_VERSION); printf("adb server is out of date. killing...\n"); fd = _adb_connect("host:kill"); adb_close(fd); /* XXX can we better detect its death? */ adb_sleep_ms(2000); goto start_server; }
我们发现了好东东:ADB_SERVER_VERSION!找到它的声明!
#define ADB_SERVER_VERSION 26 // Increment this when we want to force users to start a new adb server
这就是版本号啊!我们离真相已经不远了!!!
我在B用户adb源码adb_client.c中加入了一条打印,希望能发现更多信息。
继续测试
5. 再次启动B adb server
[~/macan/Honeycomb]adb start-server*Yagami* version: 31 ADB_SERVER_VERSION: 26adb server is out of date. killing...* daemon started successfully *
同样,我们发现B又kill掉了A的server。不过此时的我们已经发现了原因,一个版本号是26,一个版本号是31。。。
6. 好吧,让我们看看A,B,C分别都是神码版本吧。。。
macan@BG2BLT06:~$ adb versionAndroid Debug Bridge version 1.0.31
[~/macan/Honeycomb]adb versionAndroid Debug Bridge version 1.0.26
cwen@BG2BLT06:~$ adb versionAndroid Debug Bridge version 1.0.26
怪不的B,C两个能兼容,因为版本号一样啊。。。
到这里还没完呢,原因找到了,解决方法还木有给呢。。。
其实很简单啦,统一版本号呗。
方法一:
在tftp的/usr/bin下面存放一个adb,保证每个用户的$PATH里面/usr/bin的路径在最前面,这样大家就可以统一用这个adb,版本当然一样。
方法二:
如果每个用户想用自己的adb server,可能有时自己会修改源码,那么大家就统一一个版本号,修改ADB_SERVER_VERSION这个宏即可。
ps:还有一点测试时候用的其他命令,就当看着玩吧。
在进入DMP Board查看daemon进程
ps | grep adbdroot 735 1 7484 900 ffffffff 2ac1ec30 S /sbin/adbd
查看5037端口号
netstat -apn|grep 5037tcp 0 0 127.0.0.1:5037 0.0.0.0:* LISTEN 15861/adb
查看进程15861
ps aux | grep 15861chenwei 15861 0.0 0.0 29144 1512 pts/27 Sl 11:37 0:00 adb fork-server serverchenwei 22605 0.0 0.0 8956 876 pts/0 S+ 11:55 0:00 grep --color=auto 15861