老李推荐: 第8章4节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动AndroidDebugBridge 3
261行创建一个和ADB服务器监听的Socket端口的一个异步非阻塞SocketChannel连接,该连接就是专门用于往后往ADB服务器发送命令用的,返回给deviceMonitorLoop方法后会被保存到mMainAdbConnection中,请大家记住它,我们往下会用到它。
第二步关于如何调用startAdb来开启ADB服务器是上一节的重点,所以我们不会重新分析了。
第三步是向ADB服务器发送设备监控命令,我们跳进去:
272 private boolean sendDeviceListMonitoringRequest()
273 throws TimeoutException, IOException
274 {
275 byte[] request =
AdbHelper.formAdbRequest("host:track-devices");
276 try
277 {
278 AdbHelper.write(this.mMainAdbConnection, request);
279
280 AdbHelper.AdbResponse resp = AdbHelper.readAdbResponse(this.mMainAdbConnection, false);
281
282
283 if (!resp.okay)
284 {
285 Log.e("DeviceMonitor", "adb refused request: " + resp.message);
286 }
287
288 return resp.okay;
289 } catch (IOException e) {
290 Log.e("DeviceMonitor", "Sending Tracking request failed!");
291 this.mMainAdbConnection.close();
292 throw e;
293 }
294 }
代码8-4-5 DeviceMonitor - sendDeviceListMonitoringRequest
整个方法的功能就是去构建一个发送到ADB服务器请求服务的命令,然后发送,读取结果,返回,错误处理:
- 275行: 构建发送给ADB服务器的请求获得设备监控服务的命令字串”host:track-devices”, 我们在下一章描述MonkeyDevice实现原理的时候会详细描述formAdbRequest是如何构造一个ADB服务请求命令的
- 278行: 通过AdbHelper的write方法往上面建立的与ADB服务器连接的SocketChannel mMainAdbConnection写入该请求,而该write方法最终调用的是SocketChannel的write方法往Socket写数据,我们在下一章描述MonkeyDevice实现原理的时候会详细描述
- 280行: 与写Socket对应的就是读Socket了,发送完命令后就会调用AdbHelper的readAdbResponse方法来把命令发送的结果读取出来返回了, 注意这里读回来的只是命令发送的成功还是失败信息,并不是返回的设备列表。我们在下一章描述MonkeyDevice实现原理的时候会详细描述readAdbResponse这个方法
对应的 AdbHelper相应方法的实现细节我们下一章会详尽描述。这里我们只需要清楚这个方法做的事情就是刚才提到的往ADB服务器发送”host:track-devices”命令去请求相应设备监控服务就够了。那么这个服务请求是怎么回事呢?其实这个在第一章中已经描述过,用来就是让ADB服务器周期性的往客户端,也就是往这里的DeviceMonitor线程发送设备更新列表:
host:track-devices
这个服务是以上的host:devices的一个变种,客户端和ADB服务器的连接会一直保持,当有增加/移除设备或者设备状态改变的时候会主动的往连接上的客户端发送新的设备列表信息(4字节16进制长度+内容)。这样做的话就可以允许DDMS这些工具来实时跟踪所有连接上来的设备的状态,而不需要客户端每次都去连接ADB服务器获取对应信息。
最终这个命令返回格式跟你在命令行调用ADB命令行客户端发送命令”adb devices”,返回来的就是“设备序列号 设备状态”的格式: 图8-4-2 adb devices命令返回结果
注意这里device的状态其实就是oneline, 在ddmlib的IDevice类中有相应的定义:
86 public static enum DeviceState { BOOTLOADER("bootloader"),
87 OFFLINE("offline"),
88 ONLINE("device"),
89 RECOVERY("recovery"),
90 UNAUTHORIZED("unauthorized");
91
92 private String mState;
93
94 private DeviceState(String state) {
95 this.mState = state;
96 }
代码8-4-6 IDevice - DeviceState
“host:track-devices”这个监控请求命令发送一次后就会不停的周期性获得ADB服务器发送过来的设备列表,所以前面的deviceMonitorLoop循环在第一次循环之后其实可以简化成以下几行代码:
155 private void deviceMonitorLoop()
156 {
157 do
158 {
159 try
160 {
...
187 if (this.mMonitoring)
188 {
189 int length = readLength(this.mMainAdbConnection,
this.mLengthBuffer);
190
191 if (length >= 0)
192 {
193 processIncomingDeviceData(length);
194
195
196 this.mInitialDeviceListDone = true;
197 }
198 }
199 }
...
206 } while (!this.mQuit);
207 }
代码8-4-3 DeviceMonitor - deviceMonitorLoop简化版
所以最终重点就是调用processIncomingDeviceData来处理更新后的设备列表。
296 private void processIncomingDeviceData(int length) throws IOException
297 {
298 ArrayList<Device> list = new ArrayList();
299
300 if (length > 0) {
301 byte[] buffer = new byte[length];
302 String result = read(this.mMainAdbConnection, buffer);
303
304 String[] devices = result.split("\n");
305
306 for (String d : devices) {
307 String[] param = d.split("\t");
308 if (param.length == 2)
309 {
310 Device device = new Device(this, param[0],
IDevice.DeviceState.getState(param[1]));
311
312
313
314 list.add(device);
315 }
316 }
317 }
318
319
320 updateDevices(list);
321 }
代码8-4-4 DeviceMonitor - processIncomingDeviceData
方法体首先在302行获得ADB服务器发送过来的”设备序列号 状态”的设备列表,然后在后续几行循环将每个设备的序列号的和状态解析出来,然后就是根据序列号和状态创建一个代表该设备的Device对象并把其存储到一个列表list里面,最后就是调用updateDevices方法进行下一步动作了。这了我们先简单看下Device的构造函数,至于它的详细分析将会留到下一章。
677 Device(DeviceMonitor monitor, String serialNumber, IDevice.DeviceState deviceState)
678 {
679 this.mMonitor = monitor;
680 this.mSerialNumber = serialNumber;
681 this.mState = deviceState;
682 }
代码8-4-5 Device构造函数
Device的构造函数非常简单,就是把上面传进来的DeviceMonitor实例,ADB服务器主动发送过来的设备的序列号字串,以及设备当前的状态给保存起来到对应的成员变量中而已。这里要注意的的士mSerialNumber和mState,在往下分析“启动Monkey“的时候我们需要用到。
我们往下继续分析updateDevices这个方法。这个方法的代码稍微长那么一点点,我们把它分开来分析:
323 /**
324 * Updates the device list with the new items received from the monitoring service.
325 */
326 private void updateDevices(ArrayList<Device> newList) {
327 // because we are going to call mServer.deviceDisconnected which will acquire this lock
328 // we lock it first, so that the AndroidDebugBridge lock is always locked first.
329 synchronized (AndroidDebugBridge.getLock()) {
330 // array to store the devices that must be queried for information.
331 // it's important to not do it inside the synchronized loop as this could block
332 // the whole workspace (this lock is acquired during build too).
333 ArrayList<Device> devicesToQuery = new ArrayList<Device>();
334 synchronized (mDevices) {
335 // For each device in the current list, we look for a matching the new list.
336 // * if we find it, we update the current object with whatever new information
337 // there is
338 // (mostly state change, if the device becomes ready, we query for build info).
339 // We also remove the device from the new list to mark it as "processed"
340 // * if we do not find it, we remove it from the current list.
341 // Once this is done, the new list contains device we aren't monitoring yet, so we
342 // add them to the list, and start monitoring them.
343
344 for (int d = 0 ; d < mDevices.size() ;) {
345 Device device = mDevices.get(d);
346
347 // look for a similar device in the new list.
348 int count = newList.size();
349 boolean foundMatch = false;
350 for (int dd = 0 ; dd < count ; dd++) {
351 Device newDevice = newList.get(dd);
352 // see if it matches in id and serial number.
353 if (newDevice.getSerialNumber().equals(device.getSerialNumber())) {
354 foundMatch = true;
355
356 // update the state if needed.
357 if (device.getState() != newDevice.getState()) {
358 device.setState(newDevice.getState());
359 device.update(Device.CHANGE_STATE);
360
361 // if the device just got ready/online, we need to start
362 // monitoring it.
363 if (device.isOnline()) {
364 if (AndroidDebugBridge.getClientSupport()) {
365 if (!startMonitoringDevice(device)) {
366 Log.e("DeviceMonitor",
367 "Failed to start monitoring "
368 + device.getSerialNumber());
369 }
370 }
371
372 if (device.getPropertyCount() == 0) {
373 devicesToQuery.add(device);
374 }
375 }
376 }
377
378 // remove the new device from the list since it's been used
379 newList.remove(dd);
380 break;
381 }
382 }
383
384 if (!foundMatch) {
385 // the device is gone, we need to remove it, and keep current index
386 // to process the next one.
387 removeDevice(device);
388 mServer.deviceDisconnected(device);
389 } else {
390 // process the next one
391 d++;
392 }
393 }
...
}
代码8-4-5 DeviceMonitor - updateDevices处理移除和状态改变设备