老李推荐:第8章3节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动AndroidDebugBridge
用户在命令行运行monkeyrunner命令来执行测试脚本的时候ADB服务器有可能还没有起来,AndroidDebugBridge类的主要作用之一就是去开启一个新的进程来启动ADB服务器,这样我们的测试脚本才能发送命令给ADB服务器去驱动目标设备做事情,比如安装或者删除待测应用的安装包等。
MonkeyRunner在启动的过程中会牵涉到一系列的调用并关联到不同的类来做不同的事情。
图8-3-1 启动AndroidDebugBridge涉及的类关系
以上类图列出了启动AndroidDebugBridge涉及的关键类的关系,同时列出了在启动过程中每个类设计的关键成员方法和成员变量,在进入代码分析之前我们先对这些做一些描述:
- MonkeyRunnerStarter: 这个类我们在上一小节已经碰到过,它就是monkeyrunner这个jar包的入口类。它拥有的options对象我们已经分析过。这一小节我们主要分析的是从monkeyRunnerStarter构造函数引发的一系列以启动AndroidDebugBridge为目标的调用。过程中它会实例化ChimpChat这个类,并把实例通过MonkeyRunner的setChimpChat方法保存到MonkeyRunner的chimpchat这个成员变量里面保存起来。保存起来有什么用呢?大家应该注意到MonkeyRunner,ChimpChat和AdbBackend类都拥有waitForConnection这个方法,该方法一般是在我们的测试脚本最开始的时候调用的,目的是获得一个叫做AdbChimpDevice的高层抽象设备对象,这个我们在第6小节”启动Monkey”中会详细描述。这里我们主要向说明的是MonkeyRunner保存了ChimpChat实例后,它的waitForConnection方法就能通过chimpchat对象直接调用ChimpChat类的waitForConnection方法。
- ChimpChat: 这个类主要的功能是组合AdbBackend这个类来实现对AndroidDebugBridge的创建。MonkeyRunnerStarter依赖这个类来创建AdbBackend类的实例并把该实例保存起来到该类的mBackend这个成员变量里面。保存起来的目的跟上面MonkeyRunner组合ChimpChat的目的一样,这样它的waitForConnection方法就能通过mBackend这个对象来调用AdbBackend类的waitForConnection方法
- AdbBackend: 负责创建AndroidDebugBridge实例。它在自身实例化的时候会创建AndroidDebugBridge的实例并把该实例保存起来到bridge这个成员变量里面,这样只要拥有了AdbBackend实例的类就能通过bridge来获得AndroidDebugBridge维护的最新的设备列表。比如AdbBackend在实现waitForConnection方法时就调用了bridge.getDevices方法来获得所有连接上来的设备列表,然后通过设备序列号来找到目标设备来进行连接
- AndroidDebugBridge: 这个类有两个重要功能,其一是启动ADB服务器,其二是启动设备监控线程DeviceMonitor。第二点我们会在下一小节进行阐述,我们这一小节重点是分析第一点看它是怎么启动ADB服务器的。从上图中列出来的该类的成员变量和成员方法可以看到它们主要都是跟启动ADB服务器相关的,比如DEFAULT_ADB_HOST和DEFAULT_ADB_PORT变量主要是指定ADB服务器默认需要监听的地址和端口,getAdbLaunchCommand方法是去获得相应的ADB启动或者停止命令字串来启动ADB服务器(开创新进程”adb start-server”)或停止ADB服务器(“adb kill-server”)
下面我们通过源码分析来阐述个中原理。在通过上一节分析的处理好命令行参数之后,monkeyrunner入口main函数的下一步就是去尝试根据这些参数来调用MonkeyRunnerStarter的构造函数:
178 public static void main(String[] args) {
179 MonkeyRunnerOptions options =
MonkeyRunnerOptions.processOptions(args);
180
181 if (options == null) {
182 return;
183 }
184
185
186 replaceAllLogFormatters(MonkeyFormatter.DEFAULT_INSTANCE,
options.getLogLevel());
187
188 MonkeyRunnerStarter runner =
new MonkeyRunnerStarter(options);
189 int error = runner.run();
190
191
192 System.exit(error);
193 }
194 }
代码8-3-1 初始化MonkeyRunnerStarter
其中的options参数就是上一节最后根据所有参数创建的MonkeyRunnerOptions对象,里面保存了所有的参数。往下我们进入MonkeyRunnerStarter的构造函数:
代码8-3-2 MonkeyRunnerStarter构造函数
55 public MonkeyRunnerStarter(MonkeyRunnerOptions options)
56 {
57 Map<String, String> chimp_options = new TreeMap();
58 chimp_options.put("backend", options.getBackendName());
59 this.options = options;
60 this.chimp = ChimpChat.getInstance(chimp_options);
61 MonkeyRunner.setChimpChat(this.chimp);
62 }
仅从这个方法的几行代码我们可以看到它其实做的事情就是去根据‘backend’来初始化ChimpChat 并把该实例保存到MonkeyRunnerStarter的chimp成员变量中,同时也会调用MonkeyRunner的静态方法setChimpChat把该ChimpChat对象设置到MonkeyRunner的静态成员变量里面,为什么说它一定是静态成员变量呢?因为第61行保存该实例调用的是MonkeyRunner这个类的方法,而不是一个实例,所以该方法肯定就是静态的,而一个静态方法里面的成员函数也必然是静态的。大家跳进去MonkeyRunner这个类就可以看到:
51 static void setChimpChat(ChimpChat chimp)
52 {
53 chimpchat = chimp;
54 }
代码8-3-3 MonkeyRunner - setChimpChat