🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
如前文《[谁动了我的截图?--Monkeyrunner takeSnapshot方法源码跟踪分析](http://blog.csdn.net/zhubaitian/article/details/40262831)》所述,本文主要会尝试描述android的自动化测试框架MonkeyRunner究竟是如何和目标设备进行通信的。 在上一篇文章中我们其实已经描述了其中一个方法,就是通过adb协议发送adb服务器请求的方式驱动android设备的adbd守护进程去获取FrameBuffer的数据生成屏幕截图。那么MonkeyRunner还会用其他方式和目标设备进行通信吗?答案是肯定的,且看我们一步步分析道来。 ## 1.概述 MonkeyRunner和目标设备打交道都是通过ChimpChat层进行封装分发但最终是在ddmlib进行处理的,其中囊括的方法大体如下: - **发送monkey命令**:MonkeyRunner先通过adb shell发送命令"monkey -port  12345"在目标机器上启动monkey以监听端口接受连接,然后MonkeyRunner通过连接该端口建立socket并发送monkey命令。所有与界面相关的操作都是通过这种方式发送到目标机器的。 - **发送adb协议请求**:通过发送adb协议请求来与目标设备通信的,详情请查看<<[谁动了我的截图?--Monkeyrunner takeSnapshot方法源码跟踪分析](http://blog.csdn.net/zhubaitian/article/details/40262831)>>和<<[adb概览及协议参考](http://blog.csdn.net/zhubaitian/article/details/40260783)>>,其实adb命令行客户端的所有命令最终都是通过发送遵循adb协议的请求来实现的,只是做成命令行方式方便终端用户使用而已 - **发送adb shell命令**:模拟adb命令行工具发送adb shell命令,只是不是真正的直接命令行调用adb工具,而是在每一个命令执行之前先通过上面的“**发送adb协议请求**“发送“shell:”请求建立一个和adb服务器通信的adb shell的socket连接通道,adb服务器再和目标设备的adb守护进程进行通信 以下是MonkeyDevice所有请求对应的与设备通信方式 <table cellspacing="0" cellpadding="0" class=" " style="border-collapse:collapse"><tbody><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:19px; padding:4px; background-color:rgb(191,191,191)"><p align="center" style="margin-top:0px; margin-bottom:0px; text-align:center"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px"><strong>请求</strong></span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:19px; padding:4px; background-color:rgb(191,191,191)"><p align="center" style="margin-top:0px; margin-bottom:0px; text-align:center"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px"><strong>是否需要和目标设备通信</strong></span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:19px; padding:4px; background-color:rgb(191,191,191)"><p align="center" style="margin-top:0px; margin-bottom:0px; text-align:center"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px"><strong>通信方式</strong></span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:19px; padding:4px; background-color:rgb(191,191,191)"><p align="center" style="margin-top:0px; margin-bottom:0px; text-align:center"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px"><strong>注解</strong></span></p></td></tr><tr><td colspan="4" valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:383px; height:13px; padding:4px; background-color:rgb(255,192,114)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px"><strong>发送</strong></span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px"><strong>adb shell</strong></span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px"><strong>命令</strong></span></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:28px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">getSystemProperty</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:28px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:28px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb shell</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:28px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:46px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">installPackage</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:46px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:46px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb shell</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:46px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">传送数据时发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">协议请求,发送安装命令时使用</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb shell</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">startActivity</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb shell</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:29px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">broadcastIntent</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:29px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:29px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb shell</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:29px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">instrument</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb shell</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:34px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">shell</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:34px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:34px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb shell</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:34px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令为空,所以相当于直接执行</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">”adb shell “</span></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">removePackage</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb shell</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td colspan="4" valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:383px; height:13px; padding:4px; background-color:rgb(255,192,114)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">monkey</span><span style="font-size:11px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">getProperty</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">monkey</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">  </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">wake</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">monkey</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">  </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; line-height:26px"><span style="font-size:14px; line-height:normal; font-family:Arial; letter-spacing:0px; color:rgb(54,46,43)">dispose </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-family:Heiti SC Light; font-size:10px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">monkey</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">   </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">press</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">monkey</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">  </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">type</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">monkey</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">  </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">touch</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">monkey</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">  </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">drag</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">monkey</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">  </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">getViewIdList</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">monkey</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">  </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">getView</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">monkey</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">  </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">getViews</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">monkey</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">  </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">getRootView</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">monkey</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">  </span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td colspan="4" valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:383px; height:13px; padding:4px; background-color:rgb(255,192,114)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">adb</span><span style="font-size:11px; font-family:'Heiti SC Light'; letter-spacing:0px">协议请求</span></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Arial; letter-spacing:0px">takeSnapshot</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">协议请求</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:13px; padding:4px"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">reboot</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">协议命令</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:14px; padding:4px; background-color:rgb(239,239,239)"><p style="margin-top:0px; margin-bottom:0px; font-family:Helvetica; min-height:14px"><br/></p></td></tr><tr><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:36px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:11px; font-family:Monaco; letter-spacing:0px">installPackage</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:36px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">是</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:36px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">协议请求</span></p></td><td valign="top" style="border-style:solid; border-color:rgb(0,0,0); width:89px; height:36px; padding:4px"><p style="margin-top:0px; margin-bottom:0px"><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">相当于直接发送</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">adb</span><span style="font-size:10px; font-family:'Heiti SC Light'; letter-spacing:0px">命令行命令</span><span style="font-size:10px; font-family:Helvetica; letter-spacing:0px">’adb push’</span></p></td></tr></tbody></table> 分析之前请大家准备好对应的几个库的源码: ![](https://box.kancloud.cn/2016-08-15_57b1755ac570e.jpg) ## 2. 发送monkey命令 在剖析如何发送monkey命令之前,我们需要先去了解一个类,因为这个类是处理所有monkey命令的关键,这就是ChimpChat库的ChimpManager类。 我们先查看其构造函数,看它是怎么初始化的: ~~~ /* */ private Socket monkeySocket; /* */ /* */ private BufferedWriter monkeyWriter; /* */ /* */ private BufferedReader monkeyReader; /* */ /* */ /* */ public ChimpManager(Socket monkeySocket) /* */ throws IOException /* */ { /* 62 */ this.monkeySocket = monkeySocket; /* 63 */ this.monkeyWriter = new BufferedWriter(new OutputStreamWriter(monkeySocket.getOutputStream())); /* */ /* 65 */ this.monkeyReader = new BufferedReader(new InputStreamReader(monkeySocket.getInputStream())); /* */ } ~~~ 初始化所做的事情如下 - 把构造函数传进来的monkeySocket这个socket对象保存起来,往下会分析这个socket是如何创立的 - 初始化monkeyWriter这个BufferedWriter,今后往monkey的socket发送命令的时候用的就是它 - 初始化monkeyReader这个BufferedReader,今后从monkey的socket读返回的时候用的就是它 好,那么现在我们返回来看这个类是什么时候实例化的。请定位到AdbChimpDevice的构造函数: ~~~ /* */ private ChimpManager manager; /* */ /* */ public AdbChimpDevice(IDevice device) /* */ { /* 70 */ this.device = device; /* 71 */ this.manager = createManager("127.0.0.1", 12345); /* */ /* 73 */ Preconditions.checkNotNull(this.manager); /* */ } ~~~ 可以看到ChimpManager是在AdbChimpDevice构造的时候已经开始初始化的了,初始化传入的地址是"127.0.0.1"和端口是12345,这个是在下面分析的createManager这个方法中创建socket用的,也就是我们上面提到的monkeySocket.在继续之前这里我们先整理下思路,结合上一篇文章,我们看到几个重要的类的初始化流程是这样的: - MonkeyRunner在启动的时候会先启动MonkeyRunnerStarter这个类,该类的构造函数调用ChimpChat的getInstance方法实例化ChimpChat. - ChimpChat的getInstance方法会先实例化AdbBackend这个类,然后构建 ChimpChat自身这个实例 - 用户调用MonkeyRunner.waitForConnection()方法初始化MonkeyDevice - 以上的waitForConnection()又调用的是ChimpChat的waitforConnection()方法 - ChimpChat的waitForConnection方法调用的是AdbBackend的waitForConnection方法最终会findAttachedDevice找到目标设备然后用该设备初始化AdbChimpDevice 根据以上的流程我们就很清晰AdbChimpDevice其实在测试脚本一调用MonkeyRunner.waitForConnection方法的时候就已经会初始化的了,也就是说ChimpManager也在这个时候已经初始化的了。 好,那么我们继续看AdbChimpDevice里面的方法createManager是如何对ChimpManager进行初始化的: ~~~ /* */ private ChimpManager createManager(String address, int port) { /* */ try { /* 125 */ this.device.createForward(port, port); /* */ } catch (TimeoutException e) { /* 127 */ LOG.log(Level.SEVERE, "Timeout creating adb port forwarding", e); /* 128 */ return null; /* */ } catch (AdbCommandRejectedException e) { /* 130 */ LOG.log(Level.SEVERE, "Adb rejected adb port forwarding command: " + e.getMessage(), e); /* 131 */ return null; /* */ } catch (IOException e) { /* 133 */ LOG.log(Level.SEVERE, "Unable to create adb port forwarding: " + e.getMessage(), e); /* 134 */ return null; /* */ } /* */ /* 137 */ String command = "monkey --port " + port; /* 138 */ executeAsyncCommand(command, new LoggingOutputReceiver(LOG, Level.FINE)); /* */ /* */ try /* */ { /* 142 */ Thread.sleep(1000L); /* */ } catch (InterruptedException e) { /* 144 */ LOG.log(Level.SEVERE, "Unable to sleep", e); /* */ } /* */ InetAddress addr; /* */ try /* */ { /* 149 */ addr = InetAddress.getByName(address); /* */ } catch (UnknownHostException e) { /* 151 */ LOG.log(Level.SEVERE, "Unable to convert address into InetAddress: " + address, e); /* 152 */ return null; /* */ } /* */ /* */ /* */ /* */ /* */ /* 159 */ boolean success = false; /* 160 */ ChimpManager mm = null; /* 161 */ long start = System.currentTimeMillis(); /* */ /* 163 */ while (!success) { /* 164 */ long now = System.currentTimeMillis(); /* 165 */ long diff = now - start; /* 166 */ if (diff > 30000L) { /* 167 */ LOG.severe("Timeout while trying to create chimp mananger"); /* 168 */ return null; /* */ } /* */ try /* */ { /* 172 */ Thread.sleep(1000L); /* */ } catch (InterruptedException e) { /* 174 */ LOG.log(Level.SEVERE, "Unable to sleep", e); /* */ } /* */ Socket monkeySocket; /* */ try /* */ { /* 179 */ monkeySocket = new Socket(addr, port); /* */ } catch (IOException e) { /* 181 */ LOG.log(Level.FINE, "Unable to connect socket", e); /* 182 */ success = false; } /* 183 */ continue; /* */ /* */ try /* */ { /* 187 */ mm = new ChimpManager(monkeySocket); /* */ } catch (IOException e) { /* 189 */ LOG.log(Level.SEVERE, "Unable to open writer and reader to socket"); } /* 190 */ continue; /* */ /* */ try /* */ { /* 194 */ mm.wake(); /* */ } catch (IOException e) { /* 196 */ LOG.log(Level.FINE, "Unable to wake up device", e); /* 197 */ success = false; } /* 198 */ continue; /* */ /* 200 */ success = true; /* */ } /* */ /* 203 */ return mm; /* */ } ~~~ 这个方法比较长,但大体做的事情如下: - 通过调用ddmlib的device类里面的createForward方法来把主机pc端本地的端口转发给目标机器端的monkey监听端口,这样子做的好处是我们通过直接连接主机pc端的转发端口发送命令就会等同于通过网络连接上目标机器的monkey监听端口来发送monkey命令 - 调用executeAsyncCommand方法发送异步adb shell命令 “monkey -port"到目标机器开启monkey并监听以上描述的端口 - 创建连接到主机pc对应目标设备monkey监听端口的monkeySocket - 把该monkeySocket传递到本章节开头说的ChimpManager构造函数对ChimpManager进行实例化 分析到这里我们可以看到monkey已经在目标机器起来了,那么我们就需要去分析MonkeyRunner是如何发送monkey命令过去控制设备的了。这里我们会以典型的press这个方法作为例子来进行阐述。 我们先看AdbChimpDevice里面的press方法: ~~~ /* */ public void press(String keyName, TouchPressType type) /* */ { /* */ try /* */ { /* 326 */ switch (3.$SwitchMap$com$android$chimpchat$core$TouchPressType[type.ordinal()]) { /* */ case 1: /* 328 */ this.manager.press(keyName); /* 329 */ break; /* */ case 2: /* 331 */ this.manager.keyDown(keyName); /* 332 */ break; /* */ case 3: /* 334 */ this.manager.keyUp(keyName); /* */ } /* */ } /* */ catch (IOException e) { /* 338 */ LOG.log(Level.SEVERE, "Error sending press event: " + keyName + " " + type, e); /* */ } /* */ } ~~~ 方法很简单,就是根据不同的按下类型来调用ChimpManager中不同的press的方法,我们这里假设用户按下的是 DOWN_AND_UP这个类型,也就是说调用的是ChimpMananer里面的press方法: ~~~ /* */ public boolean press(String name) /* */ throws IOException /* */ { /* 135 */ return sendMonkeyEvent("press " + name); /* */ } ~~~ 跟着调用sendMonkeyEvent: ~~~ /* */ private boolean sendMonkeyEvent(String command) /* */ throws IOException /* */ { /* 234 */ synchronized (this) { /* 235 */ String monkeyResponse = sendMonkeyEventAndGetResponse(command); /* 236 */ return parseResponseForSuccess(monkeyResponse); /* */ } /* */ } ~~~ 跟着调用sendMonkeyEventAndGetResponse方法: ~~~ /* */ private String sendMonkeyEventAndGetResponse(String command) /* */ throws IOException /* */ { /* 182 */ command = command.trim(); /* 183 */ LOG.info("Monkey Command: " + command + "."); /* */ /* */ /* 186 */ this.monkeyWriter.write(command + "\n"); /* 187 */ this.monkeyWriter.flush(); /* 188 */ return this.monkeyReader.readLine(); /* */ } ~~~ 以上这几个方法都是在ChimpManager这个类里面的成员方法。从最后这个sendMonkeyEventAndGetResponse方法我们可以看到它所做的事情就是用我们前面描述的monkeyWritter和monkeyReader这两个成员变量往主机pc这边的终会转发给目标机器monkey那个端口(其实就是上面的monkeySocket)进行读写操作。 ## 3. 发送adb协议请求 请查看《[谁动了我的截图?--Monkeyrunner takeSnapshot方法源码跟踪分析](http://blog.csdn.net/zhubaitian/article/details/40262831)》 ## 4. 发送adb shell命令 通过上一篇文章《[谁动了我的截图?--Monkeyrunner takeSnapshot方法源码跟踪分析](http://blog.csdn.net/zhubaitian/article/details/40262831)》的分析,我们知道MonkeyRunner分发不同的设备控制信息是在ChimpChat库的AdbChimpDevice这个类里面进行的。所以这里我就不会从头开始分析我们是怎么进入到这个类里面的了,大家不清楚的请先查看上一篇投石问路的文章再返回来看本文。 这里我们尝试以getSystemProperty这个稍微复杂点的方法为例子分析下MonkeyRunner是真么通过adb shell发送命令的,我们首先定位到AdbChimpDevice的该方法: ~~~ /* */ public String getSystemProperty(String key) /* */ { /* 224 */ return this.device.getProperty(key); /* */ } ~~~ 这里的device成员函数指的就是ddmlib库里面的Device这个类(请查看上一篇文章),那么我们进去该类看下getProperty这个方法: ~~~ /* */ public String getProperty(String name) /* */ { /* 379 */ return (String)this.mProperties.get(name); /* */ } ~~~ 该方法直接使用mProperties这个Device类的成员变量的get方法根据property的名字获得返回值,从定义可以看出这是个map: ~~~ /* 65 */ private final Map<String, String> mProperties = new HashMap(); ~~~ 且这个map是在初始化Device实例之前就已经定义好的了,因为其构造函数并没有代码提及,但是我们可以看到Device类里面有一个函数专门往这个map里面添加property: ~~~ /* */ void addProperty(String label, String value) { /* 779 */ this.mProperties.put(label, value); /* */ } ~~~ 那么这个addProperty又是在哪里被调用了呢?一番查看后发现是在ddmlib里面的GetPropertyReceiver这个类里面的processNewLines这个方法: ~~~ /* */ public void processNewLines(String[] lines) /* */ { /* 49 */ for (String line : lines) { /* 50 */ if ((!line.isEmpty()) && (!line.startsWith("#"))) /* */ { /* */ /* */ /* 54 */ Matcher m = GETPROP_PATTERN.matcher(line); /* 55 */ if (m.matches()) { /* 56 */ String label = m.group(1); /* 57 */ String value = m.group(2); /* */ /* 59 */ if (!label.isEmpty()) { /* 60 */ this.mDevice.addProperty(label, value); /* */ } /* */ } /* */ } /* */ } /* */ } ~~~ 给这个map增加所有property的地方是知道了,但是问题是什么时候增加呢?这里我们先卖个关子。 继续之前我们先要了解下ddmlib这个库里面的DeviceMonitor这个类,这个类会启动一个线程来监控所有连接到主机的设备的状态。 ~~~ /* */ boolean start() /* */ { /* 715 */ if ((this.mAdbOsLocation != null) && (sAdbServerPort != 0) && ((!this.mVersionCheck) || (!startAdb()))) { /* 716 */ return false; /* */ } /* */ /* 719 */ this.mStarted = true; /* */ /* */ /* 722 */ this.mDeviceMonitor = new DeviceMonitor(this); /* 723 */ this.mDeviceMonitor.start(); /* */ /* 725 */ return true; /* */ } ~~~ 线程的启动是在我们之前见过的AdbDebugBridge里面,一旦adb启动,就会去调用构造函数去初始化DeviceMonitor实例,并调用实例的上面这个start方法来启动一个线程。 ~~~ /* */ boolean start() /* */ { /* 715 */ if ((this.mAdbOsLocation != null) && (sAdbServerPort != 0) && ((!this.mVersionCheck) || (!startAdb()))) { /* 716 */ return false; /* */ } /* */ /* 719 */ this.mStarted = true; /* */ /* */ /* 722 */ this.mDeviceMonitor = new DeviceMonitor(this); /* 723 */ this.mDeviceMonitor.start(); /* */ /* 725 */ return true; /* */ } ~~~ 该线程会进行一个无限循环来检测设备的变动。 ~~~ private void deviceMonitorLoop() /* */ { /* */ do /* */ { /* */ try /* */ { /* 161 */ if (this.mMainAdbConnection == null) { /* 162 */ Log.d("DeviceMonitor", "Opening adb connection"); /* 163 */ this.mMainAdbConnection = openAdbConnection(); /* 164 */ if (this.mMainAdbConnection == null) { /* 165 */ this.mConnectionAttempt += 1; /* 166 */ Log.e("DeviceMonitor", "Connection attempts: " + this.mConnectionAttempt); /* 167 */ if (this.mConnectionAttempt > 10) { /* 168 */ if (!this.mServer.startAdb()) { /* 169 */ this.mRestartAttemptCount += 1; /* 170 */ Log.e("DeviceMonitor", "adb restart attempts: " + this.mRestartAttemptCount); /* */ } /* */ else { /* 173 */ this.mRestartAttemptCount = 0; /* */ } /* */ } /* 176 */ waitABit(); /* */ } else { /* 178 */ Log.d("DeviceMonitor", "Connected to adb for device monitoring"); /* 179 */ this.mConnectionAttempt = 0; /* */ } /* */ } /* */ /* 183 */ if ((this.mMainAdbConnection != null) && (!this.mMonitoring)) { /* 184 */ this.mMonitoring = sendDeviceListMonitoringRequest(); /* */ } /* */ /* 187 */ if (this.mMonitoring) /* */ { /* 189 */ int length = readLength(this.mMainAdbConnection, this.mLengthBuffer); /* */ /* 191 */ if (length >= 0) /* */ { /* 193 */ processIncomingDeviceData(length); /* */ /* */ /* 196 */ this.mInitialDeviceListDone = true; /* */ } /* */ } /* */ } /* */ catch (AsynchronousCloseException ace) {}catch (TimeoutException ioe) /* */ { /* 202 */ handleExpectionInMonitorLoop(ioe); /* */ } catch (IOException ioe) { /* 204 */ handleExpectionInMonitorLoop(ioe); /* */ } /* 206 */ } while (!this.mQuit); /* */ } ~~~ 一旦发现设备有变动,该循环会立刻调用processIncomingDeviceData这个方法来更新设备信息 ~~~ /* */ private void processIncomingDeviceData(int length) throws IOException /* */ { /* 298 */ ArrayList<Device> list = new ArrayList(); /* */ /* 300 */ if (length > 0) { /* 301 */ byte[] buffer = new byte[length]; /* 302 */ String result = read(this.mMainAdbConnection, buffer); /* */ /* 304 */ String[] devices = result.split("\n"); /* */ /* 306 */ for (String d : devices) { /* 307 */ String[] param = d.split("\t"); /* 308 */ if (param.length == 2) /* */ { /* 310 */ Device device = new Device(this, param[0], IDevice.DeviceState.getState(param[1])); /* */ /* */ /* */ /* 314 */ list.add(device); /* */ } /* */ } /* */ } /* */ /* */ /* 320 */ updateDevices(list); /* */ } ~~~ 该方法首先会取得所有的device列表(类似"adb devices -l"命令获得所有device列表),然后调用updateDevices这个方法来对所有设备信息进行一次更新: ~~~ private void updateDevices(ArrayList<Device> newList) /* */ { /* 329 */ synchronized () /* */ { /* */ /* */ /* 333 */ ArrayList<Device> devicesToQuery = new ArrayList(); /* 334 */ synchronized (this.mDevices) /* */ { /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* 344 */ for (int d = 0; d < this.mDevices.size();) { /* 345 */ Device device = (Device)this.mDevices.get(d); /* */ /* */ /* 348 */ int count = newList.size(); /* 349 */ boolean foundMatch = false; /* 350 */ for (int dd = 0; dd < count; dd++) { /* 351 */ Device newDevice = (Device)newList.get(dd); /* */ /* 353 */ if (newDevice.getSerialNumber().equals(device.getSerialNumber())) { /* 354 */ foundMatch = true; /* */ /* */ /* 357 */ if (device.getState() != newDevice.getState()) { /* 358 */ device.setState(newDevice.getState()); /* 359 */ device.update(1); /* */ /* */ /* */ /* 363 */ if (device.isOnline()) { /* 364 */ if ((AndroidDebugBridge.getClientSupport()) && /* 365 */ (!startMonitoringDevice(device))) { /* 366 */ Log.e("DeviceMonitor", "Failed to start monitoring " + device.getSerialNumber()); /* */ } /* */ /* */ /* */ /* */ /* 372 */ if (device.getPropertyCount() == 0) { /* 373 */ devicesToQuery.add(device); /* */ } /* */ } /* */ } /* */ /* */ /* 379 */ newList.remove(dd); /* 380 */ break; /* */ } /* */ } /* */ /* 384 */ if (!foundMatch) /* */ { /* */ /* 387 */ removeDevice(device); /* 388 */ this.mServer.deviceDisconnected(device); /* */ } /* */ else { /* 391 */ d++; /* */ } /* */ } /* */ /* */ /* */ /* 397 */ for (Device newDevice : newList) /* */ { /* 399 */ this.mDevices.add(newDevice); /* 400 */ this.mServer.deviceConnected(newDevice); /* */ /* */ /* 403 */ if ((AndroidDebugBridge.getClientSupport()) && /* 404 */ (newDevice.isOnline())) { /* 405 */ startMonitoringDevice(newDevice); /* */ } /* */ /* */ /* */ /* 410 */ if (newDevice.isOnline()) { /* 411 */ devicesToQuery.add(newDevice); /* */ } /* */ } /* */ } /* */ /* */ /* 417 */ for (Device d : devicesToQuery) { /* 418 */ queryNewDeviceForInfo(d); /* */ } /* */ } /* 421 */ newList.clear(); /* */ } ~~~ 该方法我们关注的是最后面它会循环每个设备,然后调用queryNewDeviceForInfo这个方法去更新每个设备所有的porperty信息。 ~~~ /* */ private void queryNewDeviceForInfo(Device device) /* */ { /* */ try /* */ { /* 446 */ device.executeShellCommand("getprop", new GetPropReceiver(device)); /* */ /* */ /* 449 */ queryNewDeviceForMountingPoint(device, "EXTERNAL_STORAGE"); /* 450 */ queryNewDeviceForMountingPoint(device, "ANDROID_DATA"); /* 451 */ queryNewDeviceForMountingPoint(device, "ANDROID_ROOT"); /* */ /* */ /* 454 */ if (device.isEmulator()) { /* 455 */ EmulatorConsole console = EmulatorConsole.getConsole(device); /* 456 */ if (console != null) { /* 457 */ device.setAvdName(console.getAvdName()); /* 458 */ console.close(); /* */ } /* */ } /* */ } catch (TimeoutException e) { /* 462 */ Log.w("DeviceMonitor", String.format("Connection timeout getting info for device %s", new Object[] { device.getSerialNumber() })); /* */ /* */ } /* */ catch (AdbCommandRejectedException e) /* */ { /* 467 */ Log.w("DeviceMonitor", String.format("Adb rejected command to get device %1$s info: %2$s", new Object[] { device.getSerialNumber(), e.getMessage() })); /* */ /* */ } /* */ catch (ShellCommandUnresponsiveException e) /* */ { /* 472 */ Log.w("DeviceMonitor", String.format("Adb shell command took too long returning info for device %s", new Object[] { device.getSerialNumber() })); /* */ /* */ } /* */ catch (IOException e) /* */ { /* 477 */ Log.w("DeviceMonitor", String.format("IO Error getting info for device %s", new Object[] { device.getSerialNumber() })); /* */ } /* */ } ~~~ 到了这里我们终于看到了该方法调用了一个ddmlib库的device类里面的executeShellCommand方法来执行‘getprop'这个命令。到目前位置我们达到的目的是知道了getSystemProperty这个MonkeyDevice的api最终确实是通过发送'adb shell getporp‘命令来获得设备属性的。 但这里遗留了两个问题 - 一个是之前提到的GetPropertyReceiver这个类里面的增加property的processNewLines方法是在哪里调用的 - 一个是executeShellCommand究竟是怎么工作的 各位看官不用着急,且看我们往下分析,很快就会水落石出了。我们继续跟踪executeShellCommand这个方法,在我们的例子中其以命令'getprop'和new的GetPropertyReceiver对象实例为参数,最终会调用到Device这个类里面的executeShellCommand这个方法。注意这个GetPropertyReceiver很重要,我们往后会看到。 ~~~ /* */ public void executeShellCommand(String command, IShellOutputReceiver receiver, int maxTimeToOutputResponse) /* */ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException /* */ { /* 618 */ AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this, receiver, maxTimeToOutputResponse); /* */ } ~~~ 方法中继续把调用直接抛给AdbHelper这个工具类, ~~~ /* */ static void executeRemoteCommand(InetSocketAddress adbSockAddr, String command, IDevice device, IShellOutputReceiver rcvr, long maxTimeToOutputResponse, TimeUnit maxTimeUnits) /* */ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException /* */ { /* 378 */ long maxTimeToOutputMs = 0L; /* 379 */ if (maxTimeToOutputResponse > 0L) { /* 380 */ if (maxTimeUnits == null) { /* 381 */ throw new NullPointerException("Time unit must not be null for non-zero max."); /* */ } /* 383 */ maxTimeToOutputMs = maxTimeUnits.toMillis(maxTimeToOutputResponse); /* */ } /* */ /* 386 */ Log.v("ddms", "execute: running " + command); /* */ /* 388 */ SocketChannel adbChan = null; /* */ try { /* 390 */ adbChan = SocketChannel.open(adbSockAddr); /* 391 */ adbChan.configureBlocking(false); /* */ /* */ /* */ /* */ /* 396 */ setDevice(adbChan, device); /* */ /* 398 */ byte[] request = formAdbRequest("shell:" + command); /* 399 */ write(adbChan, request); /* */ /* 401 */ AdbResponse resp = readAdbResponse(adbChan, false); /* 402 */ if (!resp.okay) { /* 403 */ Log.e("ddms", "ADB rejected shell command (" + command + "): " + resp.message); /* 404 */ throw new AdbCommandRejectedException(resp.message); /* */ } /* */ /* 407 */ byte[] data = new byte['䀀']; /* 408 */ ByteBuffer buf = ByteBuffer.wrap(data); /* 409 */ long timeToResponseCount = 0L; /* */ /* */ for (;;) /* */ { /* 413 */ if ((rcvr != null) && (rcvr.isCancelled())) { /* 414 */ Log.v("ddms", "execute: cancelled"); /* 415 */ break; /* */ } /* */ /* 418 */ int count = adbChan.read(buf); /* 419 */ if (count < 0) /* */ { /* 421 */ rcvr.flush(); /* 422 */ Log.v("ddms", "execute '" + command + "' on '" + device + "' : EOF hit. Read: " + count); /* */ /* 424 */ break; } /* 425 */ if (count == 0) { /* */ try { /* 427 */ int wait = 25; /* 428 */ timeToResponseCount += wait; /* 429 */ if ((maxTimeToOutputMs > 0L) && (timeToResponseCount > maxTimeToOutputMs)) { /* 430 */ throw new ShellCommandUnresponsiveException(); /* */ } /* 432 */ Thread.sleep(wait); /* */ } /* */ catch (InterruptedException ie) {} /* */ } /* */ else { /* 437 */ timeToResponseCount = 0L; /* */ /* */ /* 440 */ if (rcvr != null) { /* 441 */ rcvr.addOutput(buf.array(), buf.arrayOffset(), buf.position()); /* */ } /* 443 */ buf.rewind(); /* */ } /* */ } /* */ } finally { /* 447 */ if (adbChan != null) { /* 448 */ adbChan.close(); /* */ } /* 450 */ Log.v("ddms", "execute: returning"); /* */ } /* */ } ~~~ 方法中先创建一个面向adb服务器的socket通道,然后通过发送adb协议请求的'shell:'命令获得一个adb shell然后再把相应的adb shell命令发送到该socket。从这里可以看到,“发送adb shell命令“其实是基于”发送adb协议请求“的,因为在发送命令之前需要先通过组织基于adb协议的请求”shell:“来获得adb shell。对比上一篇文章《[谁动了我的截图?--Monkeyrunner takeSnapshot方法源码跟踪分析](http://blog.csdn.net/zhubaitian/article/details/40262831)》我们可以看到“发送adb协议请求”跟“发送adb shell命名”的最大区别就是: - 发送adb协议请求:不需要初始化adb shell,直接通过构造基于adb协议的请求把命令发送出去给adb服务器。 - 发送adb shell命令:每个命令都需要先发送“adb协议请求”的“shell:”来先建立一个adb shell,然后才能够发送命令到adb服务器,再由adb服务器转发到设备端的adb守护进程或者服务。 发送完请求后最终会调用rcvr.addOutput(buf.array(),buf.arrayOffset(), buf.position())这个方法,这里的rcvr就是通过参数传进来的我们上面提到的很重要的那个GetPropertyReceiver,那么我们去看下该类下面的addOutput究竟是怎么处理返回信息的,这里要查看的是GetPropertyReceiver父类MultiLineReceiver类的成员函数addOutPut: ~~~ /* */ public final void addOutput(byte[] data, int offset, int length) /* */ { /* 53 */ if (!isCancelled()) { /* 54 */ String s = new String(data, offset, length, Charsets.UTF_8); /* */ /* */ /* */ /* 58 */ if (this.mUnfinishedLine != null) { /* 59 */ s = this.mUnfinishedLine + s; /* 60 */ this.mUnfinishedLine = null; /* */ } /* */ /* */ /* 64 */ this.mArray.clear(); /* 65 */ int start = 0; /* */ for (;;) { /* 67 */ int index = s.indexOf("\r\n", start); /* */ /* */ /* */ /* 71 */ if (index == -1) { /* 72 */ this.mUnfinishedLine = s.substring(start); /* 73 */ break; /* */ } /* */ /* */ /* */ /* 78 */ String line = s.substring(start, index); /* 79 */ if (this.mTrimLines) { /* 80 */ line = line.trim(); /* */ } /* 82 */ this.mArray.add(line); /* */ /* */ /* 85 */ start = index + 2; /* */ } /* */ /* 88 */ if (!this.mArray.isEmpty()) /* */ { /* */ /* 91 */ String[] lines = (String[])this.mArray.toArray(new String[this.mArray.size()]); /* */ /* */ /* 94 */ processNewLines(lines); /* */ } /* */ } /* */ } ~~~ 这个函数所作的事情就是把'adb shell getprop‘返回的所有信息一行一行的进行处理,注意最终处理的函数就是processNewLines。还记得这个函数吧?这个就是我们上面提到的GetPropertyReceiver这个类中用来往mProperties这个map增加property的了。迄今为止我们算是把以上留下了两个疑问给解决完了 <table cellspacing="0" cellpadding="0" width="539" class=" " style="margin:0px 0px 10px; padding:0px; border-collapse:collapse; width:668px; max-width:100%; word-wrap:break-word!important"><tbody style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important"><tr style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important"><td valign="top" width="112" height="39" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important"> </td></tr><tr style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important"><td valign="top" width="111" height="13" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important; background-color:rgb(190,192,191)"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">作者</span></p></td><td valign="top" width="112" height="13" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important; background-color:rgb(190,192,191)"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">自主博客</span></p></td><td valign="top" width="111" height="13" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important; background-color:rgb(190,192,191)"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">微信</span></p></td><td valign="top" width="112" height="13" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important; background-color:rgb(190,192,191)"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; font-family:Helvetica; letter-spacing:0px; word-wrap:break-word!important">CSDN</span></p></td></tr><tr style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important"><td valign="top" width="111" height="39" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important; background-color:rgb(227,228,228)"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">天地会珠海分舵</span></p></td><td valign="top" width="112" height="39" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; font-size:11px; font-family:Helvetica; letter-spacing:0px; word-wrap:break-word!important"><a target="_blank" href="http://techgogogo.com/">http://techgogogo.com</a></span><span style="margin:0px; padding:0px; max-width:100%; font-family:Helvetica; font-size:11px; letter-spacing:0px; word-wrap:break-word!important"/></p><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:14px; white-space:pre-wrap; font-family:Helvetica; word-wrap:break-word!important"><br style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important"/></p></td><td valign="top" width="111" height="39" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">服务号</span><span style="margin:0px; padding:0px; max-width:100%; font-size:10px; font-family:Helvetica; letter-spacing:0px; word-wrap:break-word!important">:TechGoGoGo</span></p><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">扫描码</span><span style="margin:0px; padding:0px; max-width:100%; font-size:10px; font-family:Helvetica; letter-spacing:0px; word-wrap:break-word!important">:</span></p><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:14px; white-space:pre-wrap; font-family:Helvetica; word-wrap:break-word!important"><img src="image/47cf4f9ec59b0ef1f807a6c33ab5ce5f.jpg" alt="" style="max-width:100%; margin:0px; padding:0px; height:auto!important; word-wrap:break-word!important; width:auto!important; visibility:visible!important"/></p></td><td valign="top" width="112" height="39" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; color:rgb(62,62,62); font-family:'Helvetica Neue',Helvetica,'Hiragino Sans GB','Microsoft YaHei',΢ÈíÑźÚ,Arial,sans-serif; font-size:18px; line-height:28.7999992370605px; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; color:rgb(0,0,0); font-size:11px; font-family:Helvetica; letter-spacing:0px; word-wrap:break-word!important"><a target="_blank" href="http://blog.csdn.net/zhubaitian">http://blog.csdn.net/zhubaitian</a></span><span style="margin:0px; padding:0px; max-width:100%; color:rgb(0,0,0); font-family:Helvetica; font-size:11px; letter-spacing:0px; line-height:28.7999992370605px; word-wrap:break-word!important"/></p><div><span style="margin:0px; padding:0px; max-width:100%; color:rgb(0,0,0); font-family:Helvetica; font-size:11px; letter-spacing:0px; line-height:28.7999992370605px; word-wrap:break-word!important"><br/></span></div></td></tr></tbody></table>