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. MMA

    在32位的系统上,线性地址空间可达到4GB,这4GB一般按照3:1的比例进行分配,也就是说用户进程享有前3GB线性地址空间,而内核独享最后1GB线性地址空间.由于虚拟内存的引入,每个进程都可拥有3GB ...

  2. eclipse中创建类和方法自动注释

    <?xml version="1.0" encoding="UTF-8"?><templates><template autoin ...

  3. Nginx中的信号量(信号控制)

  4. eclipse修改主题配色

    1.Java-->Editor---> Syntax Coloring修改类中的各种代码颜色 2.General-->Editors --->Text Editors

  5. YII 1.0 设置关联模型

    在model中设置如下 /* * 设置关联 */ public function relations(){ return array( 'cate'=>array(self::BELONGS_T ...

  6. Android应用的基本组件介绍和签名Android应用程序

    一.Android应用的基本组件介绍  Activity和View :Activity只能通过setContentView(View)来显示指定的组件.View组件是所有UI控件.容器控件的基类,Vi ...

  7. Windows Server 2008 R2 允许远程桌面连接这台计算机是灰色解决办法

    发现在给"远程协助"打钩时,是灰色的没法钩上,也就没办法开启.这是因为Windows Server 2008 R2的安全性已经被微软设计的很高,默认刚安装上Windows Serv ...

  8. HTTP的GET方法模拟

    进行GET方法的测试 #telnet[ ]10.1.1.11[ ]80 GET[ ]/[ ]HTTP/1.0 [两个回车] HEAD[]/[]HTTP/1.0[回车回车] http://www.cnb ...

  9. ajax数据交互(arcgis server)

    通过ajax来调用服务器map数据,来实现搜索功能. 效果: 1.我要搜索下中国移动的地理信息: 2.会搜出17条消息,然后把他们分页显示,一页6条: 3.每一页的6天数据,会在map生成一个6条ma ...

  10. redis内存占用说明

    执行info命令后,找到Memory这一栏,就可以看到内存的使用信息了,如下图: # Memory used_memory:13490096 //数据占用了多少内存(字节) used_memory_h ...