Appuim源碼剖析(Bootstrap)

Appuim源碼剖析(Bootstrap) 

SkySeraph Jan. 26th 2017java

Email:skyseraph00@163.comandroid

更多精彩請直接訪問SkySeraph我的站點www.skyseraph.com ios

 

About

Appuim

Appium 是一個自動化測試開源工具,支持 iOS 平臺和 Android 平臺上的原生應用,web 應用和混合應用。git

這裏有很關鍵一點,跨平臺。更多瞭解Appuim多平臺支持相關信息,參考官方platform-supportgithub

 

相關概念

  • 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去匹配。web

  • Appuim服務端
    包含衆多語言庫(Java, Ruby, Python, PHP, JavaScript,C#等),都實現了 Appium 對 WebDriver 協議的擴展。bootstrap

  • JSON wire protocol
    Appuim中很是重要的協議服務器

Appuim 架構

Appuim基於Nodejs編寫,基於HTTP協議,能夠當作一個相似selenium webdriver的基於移動平臺的webdriver,遵循RESTful設計風格web服務器,接受客戶端的鏈接而後在手機設備上執行命令,最後經過HTTP的響應收集命令執行的結果。session

以下爲我整理的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 

相關文章
相關標籤/搜索