通过前面的两篇文章《Appium Android Bootstrap源码分析之控件AndroidElement》和《Appium Android Bootstrap源码分析之命令解析执行》我们了解到了Appium从pc端发送过来的命令是如何定位到命令相关的控件以及如何解析执行该命令。那么我们剩下的问题就是bootstrap是怎么启动运行的,我们会通过本篇文章的分析来阐述这个问题,以及把之前学习的相关的类给串起来看它们是怎么互动的。
public class Bootstrap extends UiAutomatorTestCase { public void testRunServer() { SocketServer server; try { server = new SocketServer(4724); server.listenForever(); } catch (final SocketServerException e) { Logger.error(e.getError()); System.exit(1); } } }从代码中可以看到,这个类是继承与UiAutomatorTestCase的,这样它就能被uiautomator作为测试用例类来执行了。
public SocketServer(final int port) throws SocketServerException { keepListening = true; 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); } }它做的第一个事情是先去创建一个AndroidCommandExecutor的实例,大家应该还记得上一篇文章说到的这个类里面保存了一个静态的很重要的action到命令处理类CommandHandler的实例的映射表吧?如果没有看过的请先去看下。
public void listenForever() throws SocketServerException { Logger.debug("Appium Socket Server Ready"); ... try { client = server.accept(); Logger.debug("Client connected"); 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"); } ... }首先调用server.accept去接受appium的连接请求,连接上后就去初始化用于读取socket的BufferedReader和BufferredWriter这两个类的实例,最后进入到handleClicentData来进行真正的数据读取和处理
private void handleClientData() throws SocketServerException { try { 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(); Logger.debug("Got data from client: " + inputString); try { AndroidCommand cmd = getCommand(inputString); Logger.debug("Got command of type " + cmd.commandType().toString()); res = runCommand(cmd); Logger.debug("Returning result: " + res); } 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() + ")"); } }
private AndroidCommand getCommand(final String data) throws JSONException, CommandTypeException { return new AndroidCommand(data); }
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); } ... }
WebElement el = driver.findElement(By.name("Add note"));这里的finElement其实就是一个命令,获取控件并存放到控件哈希表就是由它对应的CommandHandler实现类Find来完成的。
public AndroidCommandResult execute(final AndroidCommand command) throws JSONException { final Hashtable<String, Object> params = command.params(); // only makes sense on a device final Strategy strategy; try { strategy = Strategy.fromString((String) params.get("strategy")); } catch (final InvalidStrategyException e) { return new AndroidCommandResult(WDStatus.UNKNOWN_COMMAND, e.getMessage()); } ... }appium支持的策略有以下几种,这其实在我们写脚本中findElement经常会指定:
public enum Strategy { CLASS_NAME("class name"), CSS_SELECTOR("css selector"), ID("id"), NAME("name"), LINK_TEXT("link text"), PARTIAL_LINK_TEXT("partial link text"), XPATH("xpath"), ACCESSIBILITY_ID("accessibility id"), ANDROID_UIAUTOMATOR("-android uiautomator");
public AndroidCommandResult execute(final AndroidCommand command) throws JSONException { final Hashtable<String, Object> params = command.params(); ... final String contextId = (String) params.get("context"); final String text = (String) params.get("selector"); final boolean multiple = (Boolean) params.get("multiple"); ... }
public AndroidCommandResult execute(final AndroidCommand command) throws JSONException { ... try { Object result = null; List<UiSelector> selectors = getSelectors(strategy, text, multiple); ... } ... }
public AndroidCommandResult execute(final AndroidCommand command) throws JSONException { ... try { Object result = null; List<UiSelector> selectors = getSelectors(strategy, text, multiple); if (!multiple) { for (final UiSelector sel : selectors) { try { Logger.debug("Using: " + sel.toString()); result = fetchElement(sel, contextId); } catch (final ElementNotFoundException ignored) { } if (result != null) { break; } } }else { List<AndroidElement> foundElements = new ArrayList<AndroidElement>(); for (final UiSelector sel : selectors) { // With multiple selectors, we expect that some elements may not // exist. try { Logger.debug("Using: " + sel.toString()); List<AndroidElement> elementsFromSelector = fetchElements(sel, contextId); foundElements.addAll(elementsFromSelector); } catch (final UiObjectNotFoundException ignored) { } } if (strategy == Strategy.ANDROID_UIAUTOMATOR) { foundElements = ElementHelpers.dedupe(foundElements); } result = elementsToJSONArray(foundElements); } ... }而fetchElement最终调用的控件哈希表类的getElements:
private ArrayList<AndroidElement> fetchElements(final UiSelector sel, final String contextId) throws UiObjectNotFoundException { return elements.getElements(sel, contextId); }AndroidElementHash的这个方法我们在前一篇文章《 Appium Android Bootstrap源码分析之控件AndroidElement》已经分析过,我们今天再来温习一下.
WebElement addNote = driver.findElement(By.name("Add note"));
WebElement el = driver.findElement(By.className("android.widget.ListView")).findElement(By.name("Note1"));以上的脚本会先尝试找到Note1这个日记的父控件ListView,并把这个控件保存到控件哈希表,然后再根据父控件的哈希表键值以及子控件的选择子找到想要的Note1:
public AndroidElement addElement(final UiObject element) { counter++; final String key = counter.toString(); final AndroidElement el = new AndroidElement(key, element); elements.put(key, el); return el; }
作者 | 自主博客 | 微信服务号及扫描码 | CSDN |
天地会珠海分舵 | http://techgogogo.com | 服务号:TechGoGoGo扫描码: | http://blog.csdn.net/zhubaitian |