当前位置: 代码迷 >> Android >> 第8章4节《MonkeyRunner源码剖析》MonkeyRunner起动运行过程-启动AndroidDebugBridge
  详细解决方案

第8章4节《MonkeyRunner源码剖析》MonkeyRunner起动运行过程-启动AndroidDebugBridge

热度:121   发布时间:2016-04-24 12:06:38.0
第8章4节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动AndroidDebugBridge

上一节我们看到在启动AndroidDebugBridge的过程中会调用其start方法,而该方法会做2个主要的事情:

  1. 715行startAdb:开启AndroidDebugBridge
  2. 722-723行:初始化android设备监控并启动DeviceMonitor设备监控线程。

其中第一点我们上一小节已经做了详尽分析了,那么我们往下就去分析下第2点。

DeviceMonitor主要的功能就是监控是否有安卓设备拔除或者连接上来,然后对每个连接上来的设备的所有可调式进程进行监控。这个功能更多是给DDMS这个调试工具使用的,大家从AndroidDebugBridge所在的jar包ddmlib.jar应该就能猜到其实DDMS用到的也是这个jar包。

Android的每一个应用程序都是运行在独立的进程中的,且每个进程都是在自己的虚拟机(VM)中运行的,其实这个就基本上是Android操作系统的沙箱(SandBox)的概念了。目的就是以防你的进程到处乱闯搞破坏。每个进程的VM都会提供唯一的一个端口作连接调试器用。DDMS启动时会连接到AndroidDebugBridge(也就是ADB服务器了)。当一个设备被连接时,一个虚拟机在设备上启动或停止时它会通知DDMS,在DDMS和adb之间创建一个虚拟机的监控服务。一旦虚拟机运行,DDMS会通过AndroidDebugBridge的DeviceMonitor线程检索虚拟机的进程ID(pid),并通过设备上的adb守护进程打开一个连接到虚拟机的调试器。这样DDMS就能和虚拟机对话了。


图8-4-1 DDMS界面

DeviceMonitor这个类对于MonkeyRunner框架来说最重要的功能是它保存了一个代表所有连接上来的设备的Device列表,这个Device类非常的重要,基本可以说MonkeyRunner和ADB服务器的沟通最终都是通过它来做的,所以它相当于一个安卓设备在PC端的代理,我们下一章会对这个类做详细的分析。下面我们先看下DeviceMonitor这个类:


图8-4-2 DeviceMonitor类

这个类是ddmlib库的一个类,上面类图列出了DeviceMonitor关键的成员变量和方法,我们先对它们做初步的介绍,这样有利于大家更好的跟随我在下面对这个类的源代码进行分析:

  • mServer: DebugBridgeServer对象,因为需要向ADB服务器发送相应的设备监控命令,所以在DeviceMonitor初始化的时候会把一个DebugBridgeServer实例传进来保存到mServer中
  • mMainAdbConnection: 一个SocketChannel实例,DeviceMonitor在对每个设备进行监控之前需要先和ADB服务器建立链接
  • mDevices: 代表一个安卓设备的Device实例组成的一个列表。注意这个Device在整个MonkeyRunner框架中非常的重要,如前面所述,它基本上可以被看成是真实安卓设备在PC端的一个代理,很多发送给ADB服务器的命令最终都是通过它发送出去的。DeviceMonitor监控线程一旦发现新设备就会建立一个Device实例并放到mDevices列表里面,反之,如果一个设备被移除,就会从mDevices列表中移除
  • start: 一个默认权限的方法,所以可以被同一个ddmlib包里面的AndroidDebugBridge调用。它的作用是启动设备列表监控线程,线程循环主体在下面描述的deviceMonitorLoop方法里面
  • getDevices: 返回上面的mDevices设备。MonkeyRunner类的waitForConnection方法在建立与设备的连接时,最终会调用到这个方法来获得所有设备并根据设备序列号找到目标设备去连接
  • deviceMonitorLoop: 向ADB服务器发送”host:track-devices”设备监控命令来循环获取最新的字串类型的设备列表,这个命令类似于在命令行发送”adb devices”来获得当前连接的设备列表,但是不同的地方是该监控命令发送给ADB服务器后,ADB服务器会周期性的更新设备列表然后主动发送回来。每个循环完成后,如果发现ADB服务器有数据过来,都会调用下面的processIncomingDeviceData方法来作更新mDevices设备列表等事宜
  • sendDeviceListMonitoringRequest:上面的deviceMonitorLoop方法调用来往ADB服务器发送”host:track-devices”设备列表监控命令的方法
  • processIncomingDeviceData:上面的deviceMonitorLoop方法在发现ADB服务器有数据过来的时候会调用这个方法来从与ADB服务器连接的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的调试端口并建立对应的客户端监控线程来监控该进程。因为客户端机器下层的JDWP相关的代码主要是给DDMS等监控调试工具使用的,所以本书不会往下作深入分析
  • sendDeviceMonitoringRequest: startMonitoringDevice方法调用本方法来获得指定设备可调试的所有进程的PID,其实发送的就是”track-jdwp“命令

有了以上的基本认识后,我们继续往下分析其代码实现。AndroidDebugBridge启动起来后,下一步就是把这个ADB实例传到DeviceMonitor来去监测所有连接到adb服务器也就是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     do158     {159       try160       {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行,如果还没有连接上的ADB服务器的话就先连接上
  • 第二步: 168行,确保ADB服务器已经启动
  • 第三步: 183-185行,往ADB服务器发送监控命令,监控所有连接上来的移除的设备
  • 第四步: 处理所获得的监控设备列表

我们先看第一步,在上一节中我们已经看到ADB服务器的启动过程了,但是我们还没有看到ADB客户端是怎么连接上服务器的,一下的代码就是一个实例:

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行创建一个和ADB服务器监听的Socket端口的一个异步非阻塞SocketChannel连接,该连接就是专门用于往后往ADB服务器发送命令用的,返回给deviceMonitorLoop方法后会被保存到mMainAdbConnection中,请大家记住它,我们往下会用到它。

第二步关于如何调用startAdb来开启ADB服务器是上一节的重点,所以我们不会重新分析了。

第三步是向ADB服务器发送设备监控命令,我们跳进去:

272   private boolean sendDeviceListMonitoringRequest()273     throws TimeoutException, IOException274   {275     byte[] request = AdbHelper.formAdbRequest("host:track-devices");276     try277     {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     do158     {159       try160       {...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 IOException297   {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 lock328         // 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 block332             // 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 information337                 //   there is338                 //   (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 we342                 // 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 start362                                 // 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 used379                             newList.remove(dd);380                             break;381                         }382                     }383 384                     if (!foundMatch) {385                         // the device is gone, we need to remove it, and keep current index386                         // to process the next one.387                         removeDevice(device);388                         mServer.deviceDisconnected(device);389                     } else {390                         // process the next one391                         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 we396                 // process them.397                 for (Device newDevice : newList) {398                     // add them to the list399                     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建立一个客户端进行监控
  • 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 block525                         // ensure that the selector won't select() before the end of this block.526                         // @see deviceClientMonitorLoop527                         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行首先给ADB服务器发送监听请求获得所有可调试的应用进程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命令行客户端发送命令”adb jdwp”,返回来的就是所有可调式应用进程的PID,请看以下输出结果示例,其与上图8-4-1中DDMS的Devices模块打印的进程PID是一致的:


图 8-4-2 adb jdwp 命令输出

获取到设备里面运行的可调试进程PID列表后,大家应该也可以想到下一步动作就是为每一个PID,也就是为每一个进程的vm虚拟机创建一个客户端线程来通过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, IOException485   {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)勾搭。本文由天地会珠海分舵原创。转载请自觉,是否投诉维权看心情。