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

本文详细介绍了ChimpChat的getInstance方法及其实现细节,包括AndroidDebugBridge的创建过程、DeviceMonitor的工作原理以及MonkeyRunner的启动步骤。揭示了如何通过adb与Android设备交互,并监控设备状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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编译器去解析执行脚本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值