老李推荐:第8章6节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动Monkey
大家可能会觉得奇怪,为什么启动目标设备端的monkey进程会放在“运行测试脚本”这一节之后来阐述。
纵观前面整个MonkeyRunner的启动流程,我们看到并没有提及到monkey进程启动的地方。那么就奇怪了,monkey是什么时候被MonkeyRunner启动起来的呢?
我们的测试脚本一开始时几乎毫无例外的都需要执行一个调用:MonkeyRunner.waitForConnection(),如果有多个设备连接到主机的话还需要指定设备序列号,还可以指定等待连接的Timeout时间,比如:MonkeyRunner.waitForConnection(“xxx”,10000),其中xxx代表序列号,10000代表超时Timeout时10秒;如果没有指定参数的话框架会提供默认的参数。其实monkey的启动就是在waitForConnection的一些列调用中完成的。
MonkeyRunner调用waitForConnection的目的是什么呢?顾名思义,当然是等待连接上设备了。但是细想下觉得又不对,在上面MonkeyRunner启动运行过程中,不是已经在启动设备监控线程DeviceMonitor中创建了代表目标设备的Device类型的mDevices列表了吗?表明设备已经连接上来了啊。这时就算我们不运行任何monkeyrunner脚本,发送“adb devices”命令也能够列出来所有已经连接上来的设备了。那么难道waitForConnection起名字Google给起错了吗?其实不是的,这里waitForConnection确实是在等待设备的连接,只是等待的不是上面Device设备的连接,因为确实这个设备在启动设备监控线程的时候已经连接上来的了,这里等待的是去连接上AdbChimpDevice这个设备。这里大家可能迷糊了,这又是什么设备啊?这里其实有几个抽象设备类(抽象设备类在这里的意思不是说这些类是抽象的,而是说这些类代表的设备是真是设备在主机端的一个虚拟抽象),如果我做如下的解析大家应该就很好理解了:
- Device(ddmlib):代表的是通过ADB进行控制的设备
- ChimpManager(chimpchat): 代表的是通过money进行控制的设备
- AdbChimpDevice(chimpchat): 是一个更高层次的设备概念的抽象,它包含了以上的通过ADB控制的Device设备,同时也包含了上面的通过monkey进行控制的设备,当调用过来的时候AdbChimpDevice会根据究竟是需要发送命令到ADB服务器还是发送到monkey来决定使用的是哪个设备
- MonkeyDevice(monkeyrunner): 可以被测试脚本直接使用的一个高层抽象设备,它所有的API基本上都是分发到AdbChimpDevice来执行的
大家看下下面的类图中AdbChimpDevice和Device以及ChimpManager的关系就会更清楚了:AdbChimpDevice拥有两个关键的成员变量,一个是IDevice类型,其实就是Device的父类的一个实例;一个是ChimpManager类型的一个实例。在下一章分析MonkeyDevice的实现原理的时候我们就会看到MonkeyDevice过来的一个调用,比如press,最终是被分发到AdbChimpDevice,AdbChimpDevice再来决定是通过ChimpManager来往monkey发送命令还是通过Device往ADB服务器发送命令。
图8-6-1 启动Monkey过程涉及的关键类
我们先分析下类图中每个类的关系以及简要描述下MonkeyRunner是怎么启动 monkey的,然后再去分析它们相关的实现代码。
- MonkeyRunner: 每个测试脚本相信都会首先用这个类的waitForConnection方法来获得一个MonkeyDevice对象来去实现点击等操作,但此后在脚本中这个类基本上就不见踪影了。它拥有一个ChimpChat的实例变量chimpchat,上面我们已经分析过它是在MonkeyRunnerStarter实例化的时候被设置的。waitForConnection方法最终会通过chimpchat这个实例调用ChimpChat类的waitForConnection方法
- MonkeyDevice: 可以说是脚本使用最频繁的一个类,基本所有和目标设备交互的操作都是通过调用它来完成的。它拥有大量的操作目标设备的方法,比如press,takeSnapShot,type等。注意它拥有了一个iChimpDevice类型的impl实例,通过下面的分析我们会看到它其实就是AdbChimpDevice的一个实例,因为AdbChimpDevice也是实现了IChimpDevice接口的。根据上面的描述AdbChimpDevice是一个高层抽象出来的设备,它拥有ChimpManager的实例可以向monkey发送命令,也拥有Device实例可以向ADB服务器发送adb命令
- ChimpChat: 这个类我们在上面的启动流程中已经分析过,它是在MonkeyRunnerStarter实例化的过程中被实例化的,而它实例化的过程中又会启动AndroidDebugBridge和DeviceMonitor。实例化完后,MonkeyRunnerStarter构造函数最后会调用MonkeyRunner的setChimpChat静态方法把它设置到MonkeyRunner的chimpchat这个成员变量里面。ChimpChat拥有一个IChimpBackend类型的mBackend对象,其实就是一个AdbBackend对象了,因为根据前面启动流程的分析,当ChimpChat构造函数在实例化完AdbBackend对象后会将该对象保存下来到mBackend这个成员对象里面,ChimpChat的waitForConnection方法就是通过这个对象来调用AdbBackend的waitForConnection方法的
- AdbBackend:这个类同样在上面的启动流程中已经分析过,它是在实例化ChimpChat的过程中被实例化的,而它实例化的过程中又会去实例化AndroidDebugBridge,所以它持有一个AndroidDebugBridge的实例。通过这个实例AdbBackend就能调用AndroidDebugBridge的getDevices方法来获得所有已经连接上来的Device设备列表,并根据waitForConnection传进来的设备序列号来找到目标Device设备,再来实例化AdbChimpDevice这个高层抽象设备并返回该设备给上层
- AdbChimpDevice: 这个就是前面一直强调的高层设备,拥有代表ADB设备的device实例,同时也拥有代表monkey设备的manager实例。MonkeyRunner类的waitForConnection方法最终获得的就是这个类的实例,并以这个实例为参数构造MonkeyDevice对象的,构造过程中MonkeyDevice会把这个实例保存到MonkeyDevice对象的impl这个成员变量里面,所以上面描述的MonkeyDevice类的press,type,takeSnapshot等API调用就可以通过impl这个对象来调用其对应的press,type,takeSnapshot等方法,并将这些方法分发到monkey设备ChimpManager或者ADB设备Device去执行
- ChimpManager: 代表了一个monkey设备,也就是说所有monkey相关的请求都是发送到ChimpManager来进行处理的。它拥有几个专门和monkey通信的成员变量,比如monkeSocket是用来和monkey进行连接的;monkeyWriter是用来往该socket写数据的,也就是发送请求的;monkeyReader是用来往该socket读数据的,也就是读取请求结果的。同时它拥有很多monkey相关的调用,比如touch等
- AndroidDebugBridge: 这个类同样在前面的MonkeyRunner启动流程中已经分析过它是怎么启动起来的,前面描述的它的主要功能是将ADB服务器和DeviceMonitor给启动起来。在这一节它的主要功能将会是因为它维护了mDeviceMonitor这个DeviceMonitor的实例,所以能通过它来获得DeviceMonitor维护的最新的Device列表。为什么要获得这个列表,请看上面AdbBackend类的描述
- DeviceMonitor: 这个类在这里的主要功能就是提供了getDevices这个方法来获得它维护的最新的Device设备列表