它是个http服务器,它专门接收从客户端通过基于http的REST协议发送过来的命令
他是bootstrap客户端:它接收到客户端的命令后,需要想办法把这些命令发送给目标安卓机器的bootstrap来驱动uiatuomator来做事情
通过上一篇文章《Appium Server 源码分析之启动运行Express http服务器》我们分析了Appium Server是如何作为一个http服务器进行工作的。那么今天我们就要分析第二点,Appium Server是怎么作为bootstrap的客户端来向目标安卓机器的bootstrap发送命令以驱动uiautomator框架来做事情的
1. MVC设计模式中的Controller及路由Routing
在我们上一篇文章描述appium server在启动http服务器的过程中,实例化appium 服务器后,下一步就是要设置好从client端过来的请求的数据路由了:
var main = function (args, readyCb, doneCb) {
...
routing(appiumServer);
...
}
这里大家要有MVC设计模式这个背景知识,我相信大家做过界面应用或者网站编程的话应该很熟悉这种解藕降低依赖的著名设计模式,如果不清楚的话请自行百度谷歌。这里我会简要摘录下在我们这个http服务器中Controller扮演的角色:MVC的核心就是Controller(控制器),它负责处理http客户端传送过来的所有请求,并决定要将什么内容响应给http客户端。但Controller并不负责决定内容应该如何显示,而是将特定形态的内容响应给MVC架构,最后才由MVC架构依据响应的形态来决定如何将内容响应给http客户端。如何决定响应内容是View的责任。
nodejs的express架构就是采用了MVC框架的,所以这里才有了我们的Routing,我们先找到对应的Routing文件,然后进去看看。我们先看main.js的比较前的变量定义部分:
var http = require('http') , express = require('express') , ... , routing = require('./routing.js') 可以看到routing是在main.js所在目录的routing.js文件里导出来的,我们打开该文件: var controller = require('./controller.js'); module.exports = function (appium) { var rest = appium.rest; var globalBeforeFilter = controller.getGlobalBeforeFilter(appium); // Make appium available to all REST http requests. rest.all('/wd/*', globalBeforeFilter); routeNotYetImplemented(rest); rest.all('/wd/hub/session/*', controller.sessionBeforeFilter); rest.get('/wd/hub/status', controller.getStatus); rest.post('/wd/hub/session', controller.createSession); rest.get('/wd/hub/session/:sessionId?', controller.getSession); rest.delete('/wd/hub/session/:sessionId?', controller.deleteSession); rest.get('/wd/hub/sessions', controller.getSessions); rest.get('/wd/hub/session/:sessionId?/context', controller.getCurrentContext); rest.post('/wd/hub/session/:sessionId?/context', controller.setContext); rest.get('/wd/hub/session/:sessionId?/contexts', controller.getContexts); rest.post('/wd/hub/session/:sessionId?/element', controller.findElement); rest.post('/wd/hub/session/:sessionId?/elements', controller.findElements); rest.post('/wd/hub/session/:sessionId?/element/:elementId?/value', controller.setValue); rest.post('/wd/hub/session/:sessionId?/element/:elementId?/click', controller.doClick); ... |
路由一开始就指定了我们MVC的处理http客户端过来的Controller是controller.js这个javascript脚本
然后从上面调用穿进来的appiumServer中取出express实例并赋给rest这个变量
然后设置gloabalBeforeFilter这个控制器来处理客户端过来的而在这个routing文件中没有定义的请求的情况
在往下就是定义客户端过来的各种请求的controller处理方法了,比如最下面那个客户端请求对一个控件进行点击操作。这里就不一一列举了。这里要注意的是其中大问号的都是代表变量,真正的值是客户端传送过来的时候赋予的,所以解析的时候可以直接取elementId就能得到真正的值了。
这里有一点我觉得需要跟踪下去的是上面的controller.getGlobalBeforeFilter(appium)这个调用,因为这个方法里面设置了appium server的一个很重的成员变量:
exports.getGlobalBeforeFilter = function (appium) {
return function (req, res, next) {
req.appium = appium;
req.device = appium.device;
...
};
};
就是把appium的device这个成员变量赋予给了nodejs提供的req这个request的device这个变量,当前在没有启动一个与boostrap的session前这个值为null,但往后appium.device将会赋予android这个对象,而因为上面代码的赋值是对象赋值,所以在javascript会是指针传递,那么也就是说最后appium.device被赋值了android对象就相当于req.device被赋予了android这个对象。这个是后话,下面你会跟到这些赋值的变化的了。