Appuim源码剖析(Bootstrap)

SkySeraph Jan. 26th 2017

Email:skyseraph00@163.com

更多精彩请直接访问SkySeraph个人站点www.skyseraph.com

About

Appuim

Appium 是一个自动化测试开源工具,支持 iOS 平台和 Android 平台上的原生应用,web 应用和混合应用。

这里有很关键一点,跨平台。更多了解Appuim多平台支持相关信息,参考官方platform-support

相关概念

  • C/S 架构
    Appium 的核心是一个 web 服务器,它提供了一套 REST 的接口,接收客户端的连接,监听到命令,接着在移动设备上执行这些命令,然后将执行结果放在 HTTP 响应中返还给客户端。
  • Session机制
    Appuim的自动化测试是在一个 session 的上下文中运行,可以理解成Appuim会话。
    每一次当Appium server成功启动后,客户端的测试库(client library)会要求与Server创建一个会话(session)。
    会话的作用是为了确保能区别不同的客户端请求与不同的被测应用,每个特定的会话都有一个特定的sessionId参数。每次测试开始时,客户端将初始化一个session会话,虽然不同的语言初始化的方式不同,但是他们都要发送POST/session请求到服务器端,这些请求里面都会带有一个对象:desired capabilities ,这个时候服务器端会启动自动化session然后返回一个session ID,以后的命令都会用这个seesion ID去匹配。

  • Appuim服务端
    包含众多语言库(Java, Ruby, Python, PHP, JavaScript,C#等),都实现了 Appium 对 WebDriver 协议的扩展。

  • JSON wire protocol
    Appuim中非常重要的协议

Appuim 架构

Appuim基于Nodejs编写,基于HTTP协议,可以看成一个类似selenium webdriver的基于移动平台的webdriver,遵循RESTful设计风格web服务器,接受客户端的连接然后在手机设备上执行命令,最后通过HTTP的响应收集命令执行的结果。

如下为我整理的Appuim Android平台下架构原理图,iOS也类似,只是Bootstrap部分由Instruments替换,UiAutomator由UIAutomation替换。

如下两图参考testerhome PPT的Appuim Android和iOS平台下数据流程图。

其中,Android平台下,Android API 17+,底层调用android平台自带的UI测试框架Uiautomator;反之,调用的selendroid测试框架来完成。

Bootstrap源码剖析

源码结构

如下所示,Appuim Bootstrap部分源码结构,分UiWatchers、Bootstrap和UIAutomator三部分,非常清晰。

启动时序

Bootstrap入口类为Bootstrap.java,继承自UiAutomatorTestCase,然后开启Socket接收命令,时序如下。

类关系图

类关系如下图所示,很简单。

源码分析

Bootstrap整体分SocketServer部分,CommandHandler部分,Watchers部分和UiAutomator四部分。

  • SocketServer。完成PC Server端命令接收和解析,再通过CommandHandler的execute操作调用UiAutomator实现触控操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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");
}
  • CommandHandler,虚基类,功能类都集成自该类完成execute操作,通过HashMap映射,如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private static HashMap<String, CommandHandler> map = new HashMap<String, CommandHandler>();

  static {
map.put("waitForIdle", new WaitForIdle());
map.put("clear", new Clear());
map.put("orientation", new Orientation());
map.put("swipe", new Swipe());
map.put("flick", new Flick());
map.put("drag", new Drag());
map.put("pinch", new Pinch());
map.put("click", new Click());
map.put("touchLongClick", new TouchLongClick());
map.put("touchDown", new TouchDown());
map.put("touchUp", new TouchUp());
map.put("touchMove", new TouchMove());
map.put("getText", new GetText());
map.put("setText", new SetText());
map.put("getName", new GetName());
map.put("getAttribute", new GetAttribute());
map.put("getDeviceSize", new GetDeviceSize());
map.put("scrollTo", new ScrollTo());
map.put("find", new Find());
map.put("getLocation", new GetLocation());
map.put("getSize", new GetSize());
map.put("wake", new Wake());
map.put("pressBack", new PressBack());
map.put("pressKeyCode", new PressKeyCode());
map.put("longPressKeyCode", new LongPressKeyCode());
map.put("takeScreenshot", new TakeScreenshot());
map.put("updateStrings", new UpdateStrings());
map.put("getDataDir", new GetDataDir());
map.put("performMultiPointerGesture", new MultiPointerGesture());
map.put("openNotification", new OpenNotification());
map.put("source", new Source());
map.put("compressedLayoutHierarchy", new CompressedLayoutHierarchy());
map.put("configurator", new ConfiguratorHandler());
}

具体映射通过AndroidCommandExecutor中的execute实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public AndroidCommandResult execute(final AndroidCommand command) {
try {
Logger.debug("Got command action: " + command.action()); if (map.containsKey(command.action())) {
return map.get(command.action()).execute(command);
} else {
return new AndroidCommandResult(WDStatus.UNKNOWN_COMMAND,
"Unknown command: " + command.action());
}
} catch (final JSONException e) {
Logger.error("Could not decode action/params of command");
return new AndroidCommandResult(WDStatus.JSON_DECODER_ERROR,
"Could not decode action/params of command, please check format!");
}
}
  • Watchers,Android ANR 和 Crash,如下代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void registerAnrAndCrashWatchers() {
UiDevice.getInstance().registerWatcher("ANR", new UiWatcher() {
@Override
public boolean checkForCondition() {
UiObject window = new UiObject(new UiSelector()
.className("com.android.server.am.AppNotRespondingDialog"));
String errorText = null;
if (window.exists()) {
try {
errorText = window.getText();
} catch (UiObjectNotFoundException e) {
Log.e(LOG_TAG, "dialog gone?", e);
}
onAnrDetected(errorText);
postHandler();
return true; // triggered
}
return false; // no trigger
}
});
  • UiAutomator,通过UiAutomator执行触控操作。

Refs



文章更新, 请移步个人站点查看.

SYNC POST

========

By SkySeraph-2017

www.skyseraph.com

Appuim源码剖析(Bootstrap)的更多相关文章

  1. 豌豆夹Redis解决方案Codis源码剖析:Proxy代理

    豌豆夹Redis解决方案Codis源码剖析:Proxy代理 1.预备知识 1.1 Codis Codis就不详细说了,摘抄一下GitHub上的一些项目描述: Codis is a proxy base ...

  2. Netty学习笔记(三)——netty源码剖析

    1.Netty启动源码剖析 启动类: public class NettyNioServer { public static void main(String[] args) throws Excep ...

  3. 源码剖析@ApiImplicitParam对@RequestParam的required属性的侵入性

    问题起源 使用SpringCloud构建项目时,使用Swagger生成相应的接口文档是推荐的选项,Swagger能够提供页面访问,直接在网页上调试后端系统的接口, 非常方便.最近却遇到了一个有点困惑的 ...

  4. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  5. Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现

    声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...

  6. Apache Spark源码剖析

    Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著   ISBN 978-7-121-25420- ...

  7. 基于mybatis-generator-core 1.3.5项目的修订版以及源码剖析

    项目简单说明 mybatis-generator,是根据数据库表.字段反向生成实体类等代码文件.我在国庆时候,没事剖析了mybatis-generator-core源码,写了相当详细的中文注释,可以去 ...

  8. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

  9. SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现

    SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过serv ...

随机推荐

  1. C语言-switch语句

    switch (表达式的值) { case 1: 语句1 break; case 2: 语句2 break; case 3: 语句3 break; case 4: 语句4 break; ...... ...

  2. php 利用socket上传文件

    php 利用socket上传文件 张映 发表于 2010-06-02 分类目录: php 一,利用fsockopen来上传文件 以前我写过一篇关于socket通信原理的博文http://blog.51 ...

  3. UVa 793 - Network Connections

    题目大意:给出计算机之间的连接配置,询问某两台计算机是否相连.判断两个点是否在同一个连通分量里,用并查集处理. #include <cstdio> #define MAXN 1000000 ...

  4. UVa 10360 - Rat Attack

    题目大意:有一个1025*1025的矩阵,每个矩阵元素保存这个点上老鼠的数量.现有一种气体炸弹,能覆盖“半径”为d的矩形,在这个范围内可以消灭所有的老鼠,让你找出合适的放置炸弹的位置使的消灭的老鼠数量 ...

  5. xml的生成和发送

    s2014-04-07 10:01:05 之前学的是解析,现在需要生成xml, 然后利用蓝牙或者wifi发送到服务器 2014-04-07 10:36:34 采用dom4j创建xml报错 后来发现安卓 ...

  6. Django 自定义模版标签和过滤器

    实现自定义过滤器 1. 创建register变量 在你的模块文件中,你必须首先创建一个全局register变量,它是用来注册你自定义标签和过滤器的, 你需要在你的python文件的开始处,插入几下代码 ...

  7. CSS3 Transitions 你可能不知道的知识点

    如何临时让transition失效 我们给一个element设置了transition效果,但某些特殊情况,我们希望让transition临时失效.我们第一反应就是先移除transition设置,等其 ...

  8. 样式(Style)和主题(Theme)资源——主题资源

    与样式资源非常相似,主题资源的XML文件通常也放在/res/values 目录下,主题资源的XML文档同样以<resources.../>元素作为根元素,同样使用<style.../ ...

  9. MySQL 安装 5.0

    MySQL免安装版配置 1.下载 MySQL 免安装版 2.将 MySQL 解压到待安装目录,使用%MYSQL_HOME%表示 3.打开文件my-huge.ini另存为my.ini,在my.ini文件 ...

  10. GCD教程(三):Dispatch Sources

    接上一篇,原帖地址:http://www.dreamingwish.com/dream-2012/intro-to-grand-central-dispatch-part-iii-the-dispat ...