android自动化测试Monkeyrunner源码分析之二

来源:互联网 发布:河南省南乐县网络电视 编辑:程序博客网 时间:2024/05/21 07:06

2. ChimpChat

ChimpChat的getInstance方法如下,

public static ChimpChat getInstance(Map<String, String> options)  {    sAdbLocation = (String)options.get("adbLocation");    sNoInitAdb = Boolean.valueOf((String)options.get("noInitAdb")).booleanValue();        IChimpBackend backend = createBackendByName((String)options.get("backend"));    if (backend == null) {      return null;    }    ChimpChat chimpchat = new ChimpChat(backend);    return chimpchat;  }

1, 根据backend的名字来创建一个backend,其实就是创建一个AndroidDebugBridge

2,构造ChimpChat对象。

createBackendByName方法如下,

  private static IChimpBackend createBackendByName(String backendName)  {    if ("adb".equals(backendName)) {      return new AdbBackend(sAdbLocation, sNoInitAdb);    }    return null;  }

AdbBackend的构造方法如下,

 public AdbBackend(String adbLocation, boolean noInitAdb)  {    this.initAdb = (!noInitAdb);    if (adbLocation == null) {      adbLocation = findAdb();    }    if (this.initAdb) {      AndroidDebugBridge.init(false);    }    this.bridge = AndroidDebugBridge.createBridge(adbLocation, true);  }

创建AndroidDebugBridge之前先要确定adb程序的位置,这就是通过findAdb方法来实现的。

然后调用createBridge方法创建AndroidDebugBridge对象,

public static AndroidDebugBridge createBridge(String osLocation, boolean forceNewBridge)  {    synchronized (sLock)    {      if (sThis != null)      {        if ((sThis.mAdbOsLocation != null) && (sThis.mAdbOsLocation.equals(osLocation)) && (!forceNewBridge)) {          return sThis;        }        sThis.stop();      }      try      {        sThis = new AndroidDebugBridge(osLocation);        sThis.start();      }•••

实例化AndroidDebugBridge对象后,然后调用其start方法,启动adb

boolean start()  {    if ((this.mAdbOsLocation != null) && (sAdbServerPort != 0) && ((!this.mVersionCheck) || (!startAdb()))) {      return false;    }    this.mStarted = true;        this.mDeviceMonitor = new DeviceMonitor(this);    this.mDeviceMonitor.start();        return true;  }

1,startAdb:开启AndroidDebugBridge

2,NewDeviceMonitor并传入已经开启的adb:初始化android设备监控

3,DeviceMonitor.start:启动DeviceMonitor设备监控线程。

2.1 startAdb

startAdb的方法如下,

synchronized boolean startAdb()  {    if (this.mAdbOsLocation == null)    {      Log.e("adb", "Cannot start adb when AndroidDebugBridge is created without the location of adb.");            return false;    }    if (sAdbServerPort == 0)    {      Log.w("adb", "ADB server port for starting AndroidDebugBridge is not set.");      return false;    }    int status = -1;        String[] command = getAdbLaunchCommand("start-server");    String commandString = Joiner.on(',').join(command);    try    {      Log.d("ddms", String.format("Launching '%1$s' to ensure ADB is running.", new Object[] { commandString }));      ProcessBuilder processBuilder = new ProcessBuilder(command);      if (DdmPreferences.getUseAdbHost())      {        String adbHostValue = DdmPreferences.getAdbHostValue();        if ((adbHostValue != null) && (!adbHostValue.isEmpty()))        {          Map<String, String> env = processBuilder.environment();          env.put("ADBHOST", adbHostValue);        }      }      Process proc = processBuilder.start();            ArrayList<String> errorOutput = new ArrayList();      ArrayList<String> stdOutput = new ArrayList();      status = grabProcessOutput(proc, errorOutput, stdOutput, false);    }    catch (IOException ioe)    {      Log.e("ddms", "Unable to run 'adb': " + ioe.getMessage());    }    catch (InterruptedException ie)    {      Log.e("ddms", "Unable to run 'adb': " + ie.getMessage());    }    if (status != 0)    {      Log.e("ddms", String.format("'%1$s' failed -- run manually if necessary", new Object[] { commandString }));            return false;    }    Log.d("ddms", String.format("'%1$s' succeeded", new Object[] { commandString }));    return true;  }

1,准备好启动db server的command字串

2,通过ProcessBuilder启动command字串指定的adb server

adb服务器进程已经运行起来了。AndroidDebugBridge启动起来后,下一步就是把这个adb实例传到DeviceMonitor来去监测所有连接到adb服务器也就是pc主机端的android设备的状态。

2.2启动DeviceMonitor设备监控线程

流程图如下,


DeviceMonitor的构造方法如下,

DeviceMonitor(AndroidDebugBridge server)  {    this.mServer = server;        this.mDebuggerPorts.add(Integer.valueOf(DdmPreferences.getDebugPortBase()));  }

Start方法如下,

void start()  {    new Thread("Device List Monitor")    {      public void run()      {        DeviceMonitor.this.deviceMonitorLoop();      }    }.start();  }

另开一个线程无限循环来检测设备的状态。

private void deviceMonitorLoop()  {    do    {      try      {        if (this.mMainAdbConnection == null)        {          Log.d("DeviceMonitor", "Opening adb connection");          this.mMainAdbConnection = openAdbConnection();          if (this.mMainAdbConnection == null)          {            this.mConnectionAttempt += 1;            Log.e("DeviceMonitor", "Connection attempts: " + this.mConnectionAttempt);            if (this.mConnectionAttempt > 10) {              if (!this.mServer.startAdb())              {                this.mRestartAttemptCount += 1;                Log.e("DeviceMonitor", "adb restart attempts: " + this.mRestartAttemptCount);              }              else              {                Log.i("DeviceMonitor", "adb restarted");                this.mRestartAttemptCount = 0;              }            }            waitABit();          }          else          {            Log.d("DeviceMonitor", "Connected to adb for device monitoring");            this.mConnectionAttempt = 0;          }        }        if ((this.mMainAdbConnection != null) && (!this.mMonitoring)) {          this.mMonitoring = sendDeviceListMonitoringRequest();        }        if (this.mMonitoring)        {          int length = readLength(this.mMainAdbConnection, this.mLengthBuffer);          if (length >= 0)          {            processIncomingDeviceData(length);                        this.mInitialDeviceListDone = true;          }        }      }      catch (AsynchronousCloseException ace) {}catch (TimeoutException ioe)      {        handleExpectionInMonitorLoop(ioe);      }      catch (IOException ioe)      {        handleExpectionInMonitorLoop(ioe);      }    } while (!this.mQuit);  }

一旦发现设备有变动,该循环会立刻调用processIncomingDeviceData这个方法来更新设备信息。

private void processIncomingDeviceData(int length)    throws IOException  {    ArrayList<Device> list = new ArrayList();    if (length > 0)    {      byte[] buffer = new byte[length];      String result = read(this.mMainAdbConnection, buffer);            String[] devices = result.split("\n");      for (String d : devices)      {        String[] param = d.split("\t");        if (param.length == 2)        {          Device device = new Device(this, param[0], IDevice.DeviceState.getState(param[1]));                    list.add(device);        }      }    }    updateDevices(list);  }

该方法首先会取得所有的device列表(类似"adb devices -l"命令获得所有device列表),

然后调用updateDevices这个方法来对所有设备信息进行一次更新:

•••for (Device d : devicesToQuery) {        queryNewDeviceForInfo(d);      }•••

调用queryNewDeviceForInfo这个方法去更新每个设备所有的porperty信息

private void queryNewDeviceForMountingPoint(final Device device, final String name)    throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException  {    device.executeShellCommand("echo $" + name, new MultiLineReceiver()    {      public boolean isCancelled()      {        return false;      }            public void processNewLines(String[] lines)      {        for (String line : lines) {          if (!line.isEmpty()) {            device.setMountingPoint(name, line);          }        }      }    });  }

该方法调用了一个ddmlib库的device类里面的executeShellCommand方法来执行‘getprop'这个命令。

到目前位置我们达到的目的是知道了getSystemProperty这个MonkeyDevice的api最终确实是通过发送'adb shell getporp‘命令来获得设备属性的。

AdbHelper的executeRemoteCommand方法如下,

static void executeRemoteCommand(InetSocketAddress adbSockAddr, AdbService adbService, String command, IDevice device, IShellOutputReceiver rcvr, long maxTimeToOutputResponse, TimeUnit maxTimeUnits, @Nullable InputStream is)    throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException  {    long maxTimeToOutputMs = 0L;    if (maxTimeToOutputResponse > 0L)    {      if (maxTimeUnits == null) {        throw new NullPointerException("Time unit must not be null for non-zero max.");      }      maxTimeToOutputMs = maxTimeUnits.toMillis(maxTimeToOutputResponse);    }    Log.v("ddms", "execute: running " + command);        SocketChannel adbChan = null;    try    {      adbChan = SocketChannel.open(adbSockAddr);      adbChan.configureBlocking(false);      setDevice(adbChan, device);            byte[] request = formAdbRequest(adbService.name().toLowerCase() + ":" + command);      write(adbChan, request);            AdbResponse resp = readAdbResponse(adbChan, false);      if (!resp.okay)      {        Log.e("ddms", "ADB rejected shell command (" + command + "): " + resp.message);        throw new AdbCommandRejectedException(resp.message);      }      byte[] data = new byte[16384];      if (is != null)      {        int read;        while ((read = is.read(data)) != -1)        {          ByteBuffer buf = ByteBuffer.wrap(data, 0, read);          int written = 0;          while (buf.hasRemaining()) {            written += adbChan.write(buf);          }          if (written != read)          {            Log.e("ddms", "ADB write inconsistency, wrote " + written + "expected " + read);                        throw new AdbCommandRejectedException("write failed");          }        }      }      ByteBuffer buf = ByteBuffer.wrap(data);      buf.clear();      long timeToResponseCount = 0L;      for (;;)      {        if ((rcvr != null) && (rcvr.isCancelled()))        {          Log.v("ddms", "execute: cancelled");          break;        }        int count = adbChan.read(buf);        if (count < 0)        {          rcvr.flush();          Log.v("ddms", "execute '" + command + "' on '" + device + "' : EOF hit. Read: " + count);                    break;        }        if (count == 0)        {          try          {            int wait = 25;            timeToResponseCount += wait;            if ((maxTimeToOutputMs > 0L) && (timeToResponseCount > maxTimeToOutputMs)) {              throw new ShellCommandUnresponsiveException();            }            Thread.sleep(wait);          }          catch (InterruptedException ie) {}        }        else        {          timeToResponseCount = 0L;          if (rcvr != null) {            rcvr.addOutput(buf.array(), buf.arrayOffset(), buf.position());          }          buf.rewind();        }      }    }    finally    {      if (adbChan != null) {        adbChan.close();      }      Log.v("ddms", "execute: returning");    }  }

方法中先创建一个面向adb服务器的socket通道,然后通过发送adb协议请求的'shell:'命令获得一个adb shell

然后再把相应的adb shell命令发送到该socket。从这里可以看到,“发送adb shell命令“其实是基于”发送adb协议请求“的,

因为在发送命令之前需要先通过组织基于adb协议的请求”shell:“来获得adb shell.

 

MonkeyRunner启动步骤如下,

1,实例化MonkeyRunnerStarter时会去实例化ChimpChat这个类

2,实例化ChimpChat这个类的时候会去创建AndroidDebugBridge对象启动一个adb进程来进行与adb服务器以及目标设备的adb守护进程通讯

3,.实例化ChimpChat时还会在上面创建的adb对象的基础上创建DeviceMonitor对象并

启动一个线程来监控和维护连接到主机pc的android设备信息,因为监控设备时需要通过adb来实现的

4,最后在以上都准备好后就会尝试启动jython编译器的console或者直接调用jython编译器去解析执行脚本。

0 0