美文网首页
Android Appium源码分析

Android Appium源码分析

作者: yangzai | 来源:发表于2017-03-29 14:01 被阅读454次

    Bootstrap是一个jar包,在启动appium脚本的时候回被push到android 的/data/Local/Tmp目录下,再通过PC端命令执行该Case

    //UiAutomatorTestCase 是Android自带的测试用例集
    public class Bootstrap extends UiAutomatorTestCase {
    
      public void testRunServer() {
         ...
        SocketServer server;
        try {
        //构造方法中创建一个Socket连接监听4724端口
          server = new SocketServer(4724);
          server.listenForever(disableAndroidWatchers, acceptSSLCerts);
        } catch (final SocketServerException e) {
          Logger.error(e.getError());
          System.exit(1);
        }
      }
    }
    
     public SocketServer(final int port) throws SocketServerException {
        keepListening = true;
        //初始化Android命令集执行器,其中有一个map存放了appium所有能够使用的操作命令
        executor = new AndroidCommandExecutor();
        try {
          server = new ServerSocket(port);
          Logger.debug("Socket opened on port " + port);
        } catch (final IOException e) {
          throw new SocketServerException(
              "Could not start socket server listening on " + port);
        }
    
      }
    

    当pc端发送指令执行该测试用例时会先创建个Socket服务端连接,并调用listenForever()方法,监听4724端口,该端口与appium通信。。

     public void listenForever(boolean disableAndroidWatchers, boolean acceptSSLCerts) throws SocketServerException {
        //读取push过来的json文件并转成JsonObject
        UpdateStrings.loadStringsJson();
        if (disableAndroidWatchers) {
          Logger.debug("Skipped registering crash watchers.");
        } else {
          //设置ANR跟Crash监听器
          dismissCrashAlerts();
         //设置Dialog监听器,并保持心跳检查
          final TimerTask updateWatchers = new TimerTask() {
            @Override
            public void run() {
              try {
                watchers.check();
              } catch (final Exception e) {
              }
            }
          };
          timer.scheduleAtFixedRate(updateWatchers, 100, 100);
        }
        ...
        try {
          //等待客户端接入
          client = server.accept();
    
          in = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8"));
          out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream(), "UTF-8"));
          while (keepListening) {
          //处理客户端数据
            handleClientData();
          }
          in.close();
          out.close();
          client.close();
          Logger.debug("Closed client connection");
        } catch (final IOException e) {
          throw new SocketServerException("Error when client was trying to connect");
        }
      }
    

    当appium客户端数据发送过来的时候回转移到handleClientData()方法

     private void handleClientData() throws SocketServerException {
        try {
        //清空StringBuffer
          input.setLength(0); // clear
          String res;
          int a;
          // (char) -1 is not equal to -1.
          // ready is checked to ensure the read call doesn't block.
          while ((a = in.read()) != -1 && in.ready()) {
            input.append((char) a);
          }
          String inputString = input.toString();
         
          try {
          //将客户端数据封装成命令
            AndroidCommand cmd = getCommand(inputString);
            //执行命令
            res = runCommand(cmd);
          } catch (final CommandTypeException e) {
          //封装执行结果
            res = new AndroidCommandResult(WDStatus.UNKNOWN_ERROR, e.getMessage())
                .toString();
          } catch (final JSONException e) {
            res = new AndroidCommandResult(WDStatus.UNKNOWN_ERROR,
                "Error running and parsing command").toString();
          }
          out.write(res);
          out.flush();
        } catch (final IOException e) {
          throw new SocketServerException("Error processing data to/from socket ("
              + e.toString() + ")");
        }
      }
    

    当接收到客户端的数据时通过命令模式,将数据封装成AndroidCommand命令,再交给命令的响应者UiDevice 或UiObject来执行

    private String runCommand(final AndroidCommand cmd) {
        AndroidCommandResult res;
        // 结束
        if (cmd.commandType() == AndroidCommandType.SHUTDOWN) {
          keepListening = false;
          res = new AndroidCommandResult(WDStatus.SUCCESS, "OK, shutting down");
        } else if (cmd.commandType() == AndroidCommandType.ACTION) {
          try {
          //执行命令
            res = executor.execute(cmd);
          } 
        .....
        return res.toString();
      }
    
     public AndroidCommandResult execute(final AndroidCommand command) {
        try {
          //获取命令的action,判断客户端传来的动作类型是否是map中支持的动作
          //如果是的则由相应的动作类执行,否则异常处理
          if (map.containsKey(command.action())) {
            return map.get(command.action()).execute(command);
          }
          .............
      }
    

    封装好命令后就开始调用runCommand()执行命令.这里有个map是静态代码块初始化的,定义了appium所有可以跟UiAutomator交互的动作例如

     static {
        map.put("waitForIdle", new WaitForIdle());
        map.put("clear", new Clear());
    }
    

    Clear类就是clear动作的执行者。我们看一个简单的waitFordle命令执行

    public class WaitForIdle extends CommandHandler {
      @Override
      public AndroidCommandResult execute(final AndroidCommand command)
          throws JSONException {
        //获取客户端的参数
        final Hashtable<String, Object> params = command.params();
        long timeout = 10;
        if (params.containsKey("timeout")) {
          timeout = (Integer) params.get("timeout");
        }
        //具体的执行者执行,并返回结果
        UiDevice d = UiDevice.getInstance();
        d.waitForIdle(timeout);
        return getSuccessResult(true);
      }
    }
    

    这样我们就分析完Appium在android执行的原理了。
    总结:appium是通过自身实现一个android UiAutomator框架的case,这个case创建了一个网络通信,通过appium客户端的指令来实现UI操作,并将操作结果返回给Appium客户端


    QQ图片20180129192831.jpg

    相关文章

      网友评论

          本文标题:Android Appium源码分析

          本文链接:https://www.haomeiwen.com/subject/egueottx.html