寫在前面
本文件主要是通過斷點跟蹤對於appium原始碼,從而記錄的appium服務端的啟動過程,如有錯誤或者理解不當之處,歡迎評論提出。 appium版本:1.7.2 客戶端 appium-python-client 2018年1月 可以直接看結論,根據結論中的關鍵js檔案即可斷點跟蹤出全過程。
appium文件
主體結構
首先是官方這張圖片,這張圖片簡直涵蓋appium所有知識點!而且對於這張圖片還有中文的readme! 中文readme直通車圖上分類很清晰 基本上以appium為字首的都被封裝成了類庫,通過npm載入,在node_modules中如下圖所示,其中appium-base-driver為整個服務的基礎類庫。上圖所示的
jsonwp-proxy
、mobile-json-wire-protocal
等都在裡面。
node的express
appium的服務端採用的是express框架,express中文文件,如果之前用express建立過專案的話,會對express中的路由比較熟悉,很多時候路由的處理大概像Appium原始碼分析(3)-路由器模組這樣列出來的樣子,即通過rest.get('/wd/hub/status', controller.getStatus);
該種方式可以檢視到所有路由的處理,但是到今年18年appium的程式碼它的路由配置經過了層層呼叫。
而且作為node服務之前比較習慣入口的app.js即作為服務開啟,但是在appium中它將該入口作為express模組直接放在了appium-base-driver
中,也就是說appium原始碼的main入口並不是服務入口,那麼他們之間的關係是一個怎樣的繼承及呼叫呢?
原始碼目錄分析
假設我們想實現一個與客戶端通訊的服務,那麼主要包括服務開啟、客戶端http請求,響應函式,那麼在appium中我們將其細化一下,大概是
開啟服務->客戶端傳來請求->開啟當前測試用例會話->根據配置確定ios、android等->分別處理ios或安卓請求->ios或安卓返回後->響應客戶端吧->關閉會話。 首先看入口結構:
- build工程打包後出現的檔案 程式執行來源於build中的main.js ,基本是對lib資料夾下通過babel對於程式碼進行的轉換,所以除錯的時候建議根據main.js除錯
- config.js 配置檔案
- logger.js 日誌處理
- parser.js 終端命令的處理
- utils.js 基礎函式 main.js 中除了啟動的一些檢查,重點程式碼在於
import { server as baseServer } from 'appium-base-driver';
let router = getAppiumRouter(args);//該函式來源於appium.js
//此處router返回的是個函式 用於裝填路由路徑 該函式執行路徑位於/mjsonwp/mjsonwp.js 中routeConfiguringFunction 的返回函式
//baseServer執行之後服務開始啟動
let server = await baseServer(router, args.port, args.address);
複製程式碼
這兩行程式碼可nb厲害了……就這兩行基本就把基礎類庫溜了一圈!程式碼都是幾行幾行的,然後一調就轉一大圈 在appium.js中我們找到了以下函式
import { BaseDriver, routeConfiguringFunction, errors,
isSessionCommand, processCapabilities } from 'appium-base-driver';
function getAppiumRouter (args) {
let appium = new AppiumDriver(args);//例項化的這個類繼承了appium-base-driver!
return routeConfiguringFunction(appium);//這個函式來自於appium-base-driver
}
複製程式碼
由此成功引入基礎類庫appium-base-driver
,然後就去那裡翻吧
基礎類庫appium-base-driver
畫出的幾個紅框基本就是程式碼功能分類,其中jsonwp-proxy
和mjsonwp
是和協議相關,由於我不是很瞭解,所以不做介紹,只說明其中程式碼位置。
上邊提到程式碼轉向routeConfiguringFunction
routeConfiguringFunction
這出自mjsonwp/mjsonwp.js
,請牢牢的記住這個返回函式!!!
function routeConfiguringFunction (driver) {
//...省略一些
// return a function which will add all the routes to the driver
return function (app) {
for (let [path, methods] of _.toPairs(METHOD_MAP)) {
for (let [method, spec] of _.toPairs(methods)) {
// set up the express route handler
buildHandler(app, method, path, spec, driver, isSessionCommand(spec.command));
}
}
};
}
複製程式碼
它返回了一個函式並且裡面還傳參了app
,之前斷點打到這裡時滿腦子都是我是誰,我在哪裡,我要幹什麼
我們把程式碼往上看一下routeConfiguringFunction
的返回值返回給getAppiumRouter
再返回給let router 然後再傳給baseServer(怪我不好好學js……看個程式碼艱辛非常……)
那麼baseServer來自哪裡呢?看import,它來自appium-base-server!
main.js中呼叫了一圈再次進入appium-base-server
express中的server.js
之前說到要去找baseServer來自哪裡,終於在express/server.js中找到了 就是appium的http服務啟動的地方!所以說服務開啟的地方不在外部!在基礎類庫裡啊!
async function server (configureRoutes, port, hostname = null) {
//......
//裡面有這樣的程式碼
configureServer (app, configureRoutes) {
//然後再該函式中呼叫了
configureRoutes(app);
}
複製程式碼
所以程式碼走到這裡執行的就是那個返回函式,app是express的例項!那麼執行這一步是為了做什麼呢?這個METHOD_MAP
是最大的全路由配置!來自於mjsonwp/routes.js
,其中500行都是路由配置,經過該配置有效避免批量寫app.get()//balabala
function (app) {
for (let [path, methods] of _.toPairs(METHOD_MAP)) {
//balabalbala
}
複製程式碼
至此全路由配置裝填結束!
服務啟動
這個就沒有什麼波折了,既然server.js都找到了,就再這個檔案中,看到這裡終於看到了熟悉的node啟動~
let app = express();
let httpServer = http.createServer(app);
複製程式碼
當斷點跟到這裡時終端就可以跳出如下輸出,服務啟動啦~
[Appium] Appium REST http interface listener started on 0.0.0.0:4723
複製程式碼
總結
啟動服務執行過程
lib/main.js
執行來自於lib/appium.js
中的getAppiumRouter
函式
該函式中例項化AppiumDriver
類,同時讀取路由配置檔案,該類繼承於Appium-base-driver
庫中暴露的基類,路由配置檔案來源於Appium-base-driver
庫中/mjsonwp/routes.js
的配置
配置檔案讀取之後在/mjsonwp/mjsonwp.js
中以函式的形式返回main.js
執行baseServer
函式 該函式來自於Appium-base-driver/express/server.js
也就是說整個過程從
main.js-[呼叫]-appium.js-[呼叫]-appium-base-driver/mjsonwp-[返回]-main.js-[呼叫]-appium-base-driver/express
其實是通過層層呼叫將路由已配置的方式進行裝填並在基礎類庫appium-base-server
中啟動