第8章4节《MonkeyRunner源代码剖析》MonkeyRunner启动执行过程-启动AndroidDebugBridge
上一节我们看到在启动AndroidDebugBridge的过程中会调用其start方法,而该方法会做2个基本的事情:
- 715行startAdb:开启AndroidDebugBridge
- 722-723行:初始化android设备监控并启动DeviceMonitor设备监控线程。
当中第一点我们上一小节已经做了详尽分析了,那么我们往下就去分析下第2点。
DeviceMonitor基本的功能就是监控是否有安卓设备拔除或者连接上来,然后对每一个连接上来的设备的全部可调式进程进行监控。这个功能很多其它是给DDMS这个调试工具使用的。大家从AndroidDebugBridge所在的jar包ddmlib.jar应该就能猜到事实上DDMS用到的也是这个jar包。
Android的每一个应用程序都是执行在独立的进程中的,且每一个进程都是在自己的虚拟机(VM)中执行的,事实上这个就基本上是Android操作系统的沙箱(SandBox)的概念了。目的就是以防你的进程到处乱闯搞破坏。
每一个进程的VM都会提供唯一的一个端口作连接调试器用。DDMS启动时会连接到AndroidDebugBridge(也就是ADBserver了)。
当一个设备被连接时。一个虚拟机在设备上启动或停止时它会通知DDMS,在DDMS和adb之间创建一个虚拟机的监控服务。一旦虚拟机执行,DDMS会通过AndroidDebugBridge的DeviceMonitor线程检索虚拟机的进程ID(pid),并通过设备上的adb守护进程打开一个连接到虚拟机的调试器。这样DDMS就能和虚拟机对话了。
图8-4-1 DDMS界面
DeviceMonitor这个类对于MonkeyRunner框架来说最重要的功能是它保存了一个代表全部连接上来的设备的Device列表,这个Device类很的重要,基本能够说MonkeyRunner和ADBserver的沟通终于都是通过它来做的,所以它相当于一个安卓设备在PC端的代理,我们下一章会对这个类做具体的分析。以下我们先看下DeviceMonitor这个类:
图8-4-2 DeviceMonitor类
这个类是ddmlib库的一个类,上面类图列出了DeviceMonitor关键的成员变量和方法,我们先对它们做初步的介绍,这样有利于大家更好的尾随我在以下对这个类的源码进行分析:
- mServer: DebugBridgeServer对象,由于须要向ADBserver发送相应的设备监控命令,所以在DeviceMonitor初始化的时候会把一个DebugBridgeServer实例传进来保存到mServer中
- mMainAdbConnection: 一个SocketChannel实例。DeviceMonitor在对每一个设备进行监控之前须要先和ADBserver建立链接
- mDevices: 代表一个安卓设备的Device实例组成的一个列表。
注意这个Device在整个MonkeyRunner框架中很的重要。如前面所述。它基本上能够被看成是真实安卓设备在PC端的一个代理,很多发送给ADBserver的命令终于都是通过它发送出去的。DeviceMonitor监控线程一旦发现新设备就会建立一个Device实例并放到mDevices列表里面,反之,假设一个设备被移除,就会从mDevices列表中移除
- start: 一个默认权限的方法。所以能够被同一个ddmlib包里面的AndroidDebugBridge调用。
它的作用是启动设备列表监控线程。线程循环主体在以下描写叙述的deviceMonitorLoop方法里面
- getDevices: 返回上面的mDevices设备。MonkeyRunner类的waitForConnection方法在建立与设备的连接时。终于会调用到这种方法来获得全部设备并依据设备序列号找到目标设备去连接
- deviceMonitorLoop: 向ADBserver发送”host:track-devices”设备监控命令来循环获取最新的字串类型的设备列表,这个命令相似于在命令行发送”adb devices”来获得当前连接的设备列表,可是不同的地方是该监控命令发送给ADBserver后,ADBserver会周期性的更新设备列表然后主动发送回来。每一个循环完毕后。假设发现ADBserver有数据过来,都会调用以下的processIncomingDeviceData方法来作更新mDevices设备列表等事宜
- sendDeviceListMonitoringRequest:上面的deviceMonitorLoop方法调用来往ADBserver发送”host:track-devices”设备列表监控命令的方法
- processIncomingDeviceData:上面的deviceMonitorLoop方法在发现ADBserver有数据过来的时候会调用这种方法来从与ADBserver连接的mMainAdbConnection这个SocketChannel中把最新的字串类型设备列表读出来,然后为每一个设备实例化一个Device实例并将这些实例放到一个列表中,传送给以下的updateDevices方法来处理
- updateDevices: 在收到上面processIncomingDeviceData发送过来的最新的Device类型的设备列表后,updateDevices会依据当前持有的mDevices设备列表来检查到底哪些设备是新添加进来的,哪些是被移除掉的。哪些是状态改变了的来作相应的处理:
- 新设备: 将该设备添加到DeviceMonitor维护的mDevices设备列表里面,并调用以下的startMonitoringDevice来为该设备的每一个可调试应用进程建立一个监控线程来监控该VM进程
- 设备变成ONLINE状态: 将mDevices设备列表里面相应的设备设置成ONLINE状态。并调用以下的startMonitoringDevice来为该设备的每一个可调试应用进程建立一个监控线程来监控该VM进程
- 设备从ONLINE变成其它状态:将mDevices设备列表里面相应的设备设置成相应状态
- 移除设备: 将该设备从当前设备列表mDevices中移除掉
- startMonitoringDevice: 获得相应设备中的全部可调试VM进程的PID列表(可调试进程指的是DalvikVM中每一个启动了JDWP线程让DDMS等调试工具连接的进程),连接上每一个VM的调试端口并建立相应的client监控线程来监控该进程。由于client机器下层的JDWP相关的代码主要是给DDMS等监控调试工具使用的,所以本书不会往下作深入分析
- sendDeviceMonitoringRequest: startMonitoringDevice方法调用本方法来获得指定设备可调试的全部进程的PID,事实上发送的就是”track-jdwp“命令
有了以上的基本认识后。我们继续往下分析其代码实现。
AndroidDebugBridge启动起来后。下一步就是把这个ADB实例传到DeviceMonitor来去监測全部连接到adbserver也就是pc主机端的android设备的状态:
70 DeviceMonitor(AndroidDebugBridge server) 71 { 72 this.mServer = server; 73 74 this.mDebuggerPorts.add(Integer.valueOf(DdmPreferences.getDebugPortBase())); 75 }代码8-4-1 DeviceMonitor构造函数
保存好AndroidDebugBridge实例后,下一步就是继续AndroidDebugBridge启动函数start()启动DeviceMonitor设备监控线程:
79 void start() 80 { 81 new Thread("Device List Monitor") 82 { 83 public void run() { 84 DeviceMonitor.this.deviceMonitorLoop(); 85 } 86 }.start(); 87 }代码8-4-2 DeviceMonitor - start
第81-86行。整个方法的主体就是创建一个”Device List Monitor”的线程。线程执行方法run直接调用DeviceMonitor的deviceMonitorLoop方法来进行无限循环监控设备状态了。
155 private void deviceMonitorLoop() 156 { 157 do 158 { 159 try 160 { 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); 171 } 172 else { 173 this.mRestartAttemptCount = 0; 174 } 175 } 176 waitABit(); 177 } else { 178 Log.d("DeviceMonitor", "Connected to adb for device monitoring"); 179 this.mConnectionAttempt = 0; 180 } 181 } 182 183 if ((this.mMainAdbConnection != null) && (!this.mMonitoring)) { 184 this.mMonitoring = sendDeviceListMonitoringRequest(); 185 } 186 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 } 200 catch (AsynchronousCloseException ace) {}catch (TimeoutException ioe) 201 { 202 handleExpectionInMonitorLoop(ioe); 203 } catch (IOException ioe) { 204 handleExpectionInMonitorLoop(ioe); 205 } 206 } while (!this.mQuit); 207 }代码8-4-3 DeviceMonitor - deviceMonitorLoop
- 第一步:163行,假设还没有连接上的ADBserver的话就先连接上
- 第二步: 168行。确保ADBserver已经启动
- 第三步: 183-185行,往ADBserver发送监控命令,监控全部连接上来的移除的设备
- 第四步: 处理所获得的监控设备列表
我们先看第一步,在上一节中我们已经看到ADBserver的启动过程了。可是我们还没有看到ADBclient是怎么连接上server的,一下的代码就是一个实例:
255 private SocketChannel openAdbConnection() 256 { 257 Log.d("DeviceMonitor", "Connecting to adb for Device List Monitoring..."); 258 259 SocketChannel adbChannel = null; 260 try { 261 adbChannel = SocketChannel.open( AndroidDebugBridge.getSocketAddress()); 262 adbChannel.socket().setTcpNoDelay(true); 263 } 264 catch (IOException e) {} 265 266 return adbChannel; 267 }代码8-4-4 DeviceMonitor - openAdbConnection
261行创建一个和ADBserver监听的Socket端口的一个异步非堵塞SocketChannel连接,该连接就是专门用于往后往ADBserver发送命令用的,返回给deviceMonitorLoop方法后会被保存到mMainAdbConnection中,请大家记住它,我们往下会用到它。
第二步关于怎样调用startAdb来开启ADBserver是上一节的重点,所以我们不会又一次分析了。
第三步是向ADBserver发送设备监控命令,我们跳进去:
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
整个方法的功能就是去构建一个发送到ADBserver请求服务的命令,然后发送,读取结果。返回,错误处理:
- 275行: 构建发送给ADBserver的请求获得设备监控服务的命令字串”host:track-devices”, 我们在下一章描写叙述MonkeyDevice实现原理的时候会具体描写叙述formAdbRequest是怎样构造一个ADB服务请求命令的
- 278行: 通过AdbHelper的write方法往上面建立的与ADBserver连接的SocketChannel mMainAdbConnection写入该请求,而该write方法终于调用的是SocketChannel的write方法往Socket写数据,我们在下一章描写叙述MonkeyDevice实现原理的时候会具体描写叙述
- 280行: 与写Socket相应的就是读Socket了。发送完命令后就会调用AdbHelper的readAdbResponse方法来把命令发送的结果读取出来返回了, 注意这里读回来的仅仅是命令发送的成功还是失败信息,并非返回的设备列表。我们在下一章描写叙述MonkeyDevice实现原理的时候会具体描写叙述readAdbResponse这种方法
相应的 AdbHelper相应方法的实现细节我们下一章会详尽描写叙述。
这里我们仅仅须要清楚这种方法做的事情就是刚才提到的往ADBserver发送”host:track-devices”命令去请求相应设备监控服务就够了。那么这个服务请求是怎么回事呢?事实上这个在第一章中已经描写叙述过,用来就是让ADBserver周期性的往client,也就是往这里的DeviceMonitor线程发送设备更新列表:
host:track-devices
这个服务是以上的host:devices的一个变种,client和ADBserver的连接会一直保持。当有添加/移除设备或者设备状态改变的时候会主动的往连接上的client发送新的设备列表信息(4字节16进制长度+内容)。
这样做的话就能够同意DDMS这些工具来实时跟踪全部连接上来的设备的状态。而不须要client每次都去连接ADBserver获取相应信息。
终于这个命令返回格式跟你在命令行调用ADB命令行client发送命令”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”这个监控请求命令发送一次后就会不停的周期性获得ADBserver发送过来的设备列表,所曾经面的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行获得ADBserver发送过来的”设备序列号 状态”的设备列表,然后在兴许几行循环将每一个设备的序列号的和状态解析出来,然后就是依据序列号和状态创建一个代表该设备的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的构造函数很easy。就是把上面传进来的DeviceMonitor实例。ADBserver主动发送过来的设备的序列号字串,以及设备当前的状态给保存起来到相应的成员变量中而已。
这里要注意的的士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处理移除和状态改变设备
这一部分的代码逻辑关系是这种:
- 344行: 一个外部循环每次从上次保存下来的设备列表获得一个设备Device实例
- 350行: 再在一个内部循环从最新的设备列表中获得一个设备Device实例
- 353行:然后分别比較两个设备的序列号是否相等,相等则代表这个设备没有被移除。
- 357行: 假设设备没有被移除的话,那么推断这个设备是否是状态变化了?
- 358-373行: 变化了就须要把设备的状态改变过来
- 363-368行: 特别是在设备变成offline变成online状态后。须要去对该设备里面的可调试进程进行监控
- 372-373行: 并把该设备标识成还没有获取基本信息的状态,由于每一个已经连接的online设备都应该保存有基本的build等信息
- 379行: 假设分析完一个设备发现仅仅是状态变化了的话。最后把它从新设备列表中删除掉。由于已经分析过了。以下不须要再使用它了
- 384-388行: 假设设备已经被移除了的话(在新设备列表里面通过序列号找不到了), 那么须要把该设备移除出监控范围
以上代码是设备被移除和设备状态有更新时候的处理。那么新设备该怎么处理呢?毕竟一開始设备都是新的,这个才是关键点。
326 private void updateDevices(ArrayList<Device> newList) { ... 395 // at this point we should still have some new devices in newList, so we 396 // process them. 397 for (Device newDevice : newList) { 398 // add them to the list 399 mDevices.add(newDevice); 400 mServer.deviceConnected(newDevice); 401 402 // start monitoring them. 403 if (AndroidDebugBridge.getClientSupport()) { 404 if (newDevice.isOnline()) { 405 startMonitoringDevice(newDevice); 406 } 407 } 408 409 // look for their build info. 410 if (newDevice.isOnline()) { 411 devicesToQuery.add(newDevice); 412 } 413 } 414 } 415 416 // query the new devices for info. 417 for (Device d : devicesToQuery) { 418 queryNewDeviceForInfo(d); 419 } 420 } 421 newList.clear(); 422 }代码8-4-6 DeviceMonitor - updateDevices处理新添加设备
- 397行: 对全部剩余没有处理的新设备做一个循环
- 399行: 把该设备Device对象保留起来,下次有更新的时候须要用来跟新的设备列表做对照,正如代码5-4-5所做的事情
- 404-405行: 開始对新设备进行监控。事实上说白了就是对新设备里面的每一个可调式进程的vm建立一个client进行监控
- 418行: 获得新设备的基本信息,这个我们最后来分析
这里我们先重点看405行startMonitoringDevice:
509 private boolean startMonitoringDevice(Device device) { 510 SocketChannel socketChannel = openAdbConnection(); 511 512 if (socketChannel != null) { 513 try { 514 boolean result = sendDeviceMonitoringRequest( socketChannel, device); 515 if (result) { 516 517 if (mSelector == null) { 518 startDeviceMonitorThread(); 519 } 520 521 device.setClientMonitoringSocket(socketChannel); 522 523 synchronized (mDevices) { 524 // always wakeup before doing the register. The synchronized block 525 // ensure that the selector won't select() before the end of this block. 526 // @see deviceClientMonitorLoop 527 mSelector.wakeup(); 528 529 socketChannel.configureBlocking(false); 530 socketChannel.register(mSelector, SelectionKey.OP_READ, device); 531 } 532 533 return true; 534 } 535 } ... //省略错误处理代码 }代码8-4-7 DeviceMonitor - startMonitoringDevice
514行首先给ADBserver发送监听请求获得全部可调试的应用进程PID列表:
674 private boolean sendDeviceMonitoringRequest(SocketChannel socket, Device device) 675 throws TimeoutException, AdbCommandRejectedException, IOException { 676 677 try { 678 AdbHelper.setDevice(socket, device); 679 680 byte[] request = AdbHelper.formAdbRequest("track-jdwp"); //$NON-NLS-1$ 681 682 AdbHelper.write(socket, request); 683 684 AdbResponse resp = AdbHelper.readAdbResponse(socket, false /* readDiagString */); 685 686 if (!resp.okay) { 687 // request was refused by adb! 688 Log.e("DeviceMonitor", "adb refused request: " + resp.message); 689 } 690 691 return resp.okay; 692 } ...//省略错误处理代码 }代码8-4-8 DeviceMonitor - sendDeviceMonitoringRequest
事实上这段代码和上面的“代码8-4-5 DeviceMonitor - sendDeviceListMonitoringRequest”是相似的。仅仅是发送是要在678行先把连接切换到目标监控设备(AdbHelper.setDevice方法将在下一章进行想尽描写叙述)以及最后发送的命令变成是”track-jdwp”命令而已。
终于这个命令事实上等同于你在命令调用ADB命令行client发送命令”adb jdwp”,返回来的就是全部可调式应用进程的PID,请看以下输出结果演示样例,其与上图8-4-1中DDMS的Devices模块打印的进程PID是一致的:
图 8-4-2 adb jdwp 命令输出
获取到设备里面执行的可调试进程PID列表后。大家应该也能够想到下一步动作就是为每一个PID,也就是为每一个进程的vm虚拟机创建一个client线程来通过JDWP协议监控调试了。这也就是为什么DDMS能够动态获得每一个进程的动态信息的原因了。
进程VM虚拟机监控代码分析到这里在本书中应该就算几乎相同了,假设再往下分析的话就须要去分析DDMS很多其它的知识以及JDWP协议相关的东西了。毕竟这不是我们这本书的重点,所以分析到这里让大家对DDMS工作原理有个基本认知就好了,再往下分析一大堆不相关代码就有走题和凑字数的嫌疑了。
这里我们依据上面承诺的,还是要看看“代码8-4-6 DeviceMonitor - updateDevices处理新添加设备”中481行对新添加的设备是怎样通过调用“queryNewDeviceForInfo”这种方法来获取基本信息的。获取的又是什么信息:
442 private void queryNewDeviceForInfo(Device device) { 443 // TODO: do this in a separate thread. 444 try { 445 // first get the list of properties. 446 device.executeShellCommand( “getprop”, 447 new GetPropReceiver(device)); 448 449 queryNewDeviceForMountingPoint(device, “EXTERNAL_STORAGE”); 450 queryNewDeviceForMountingPoint(device, “ANDROID_DATA”); 451 queryNewDeviceForMountingPoint(device, “ANDROID_ROOT”); 452 453 // now get the emulator Virtual Device name (if applicable). 454 if (device.isEmulator()) { 455 EmulatorConsole console = EmulatorConsole.getConsole(device); 456 if (console != null) { 457 device.setAvdName(console.getAvdName()); 458 console.close(); 459 } 460 } 461 } ...//省略错误处理部分代码 }代码8-4-9 DeviceMonitor - queryNewDeviceForInfo
这种方法所做的事情就是:
- 446行: 首先通过Device类的executeShellCommand方法发送相似”adb shell getprop”的命令去获得全部支持的系统属性,这种方法终于调用的是AdbHelper类的executeShellCommand方法,它会接收一个专门用来对指定shell命令如getprop的返回值进行处理的接收类实例。AdbHelper的工作原理以及处理”adb shell getprop”返回结果的接收类GetPropReceiver我们在下一章”第7章 MonkeyDevice实现原理”中会进行具体阐述。
- 449-451行: 获得文件系统几个重要的挂载点,相信这也是给DDMS的File Explorer功能用的
获取完系统属性后,我们就要看下新设备的文件系统的那几个挂载点是怎么获得的了。我们进入到相应方法:
483 private void queryNewDeviceForMountingPoint(final Device device, final String name) 484 throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException 485 { 486 device.executeShellCommand("echo $" + name, new MultiLineReceiver() 487 { 488 public boolean isCancelled() { 489 return false; 490 } 491 492 public void processNewLines(String[] lines) 493 { 494 for (String line : lines) { 495 if (!line.isEmpty()) 496 { 497 device.setMountingPoint(name, line); 498 } 499 } 500 } 501 }); 502 }
代码8-4-10 DeviceMonitor - queryNewDeviceForMountingPoint
这个跟上面的发送getprop命令有相似的地方。仅仅是命令换了”adb shell $name”和返回值处理类是又一次实现的而已,但原理都一样。
这里$name换成上面调用方法形參相应的”EXTERNAL_STORAGE”,”ANDROID_DATA”和“ANDROID_ROOT”即可了。以下就是本人通过命令行执行的效果:
图8-4-4 挂载点
最后把这个几个挂载点保存起来到Device实例的mMountpoints这个映射表里面:
67 private final Map<String, String> mMountPoints = new HashMap(); ... 783 void setMountingPoint(String name, String value) { 784 this.mMountPoints.put(name, value); 785 }代码8-4-11 Device - setMountingPoint
注:很多其它文章请关注公众号:techgogogo或个人博客http://techgogogo.com。当然,也很欢迎您直接微信(zhubaitian1)勾搭。本文由天地会珠海分舵原创。转载请自觉,是否投诉维权看心情。