2. 创建Appium任务队列Work Queue
appium server和bootstrap的连接在什么时候开始建立呢?其实这个需要由appium client端来进行启动。也就是说如果你只是启动appium这个应用的话,它是不会尝试和目标安卓机器的bootstrap进行连接的,而一旦我们准备运行一个脚本的时候,appium cilent端就会立刻先发送一个创建与bootstrap回话的请求“/wd/hub/session”请求过来:
这个appium client创建session的请求所带的参数就是我们脚本中设置好的capabilities,在我的例子中是这些:
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceName","Android");
capabilities.setCapability("appPackage", "com.example.android.notepad");
capabilities.setCapability("appActivity", "com.example.android.notepad.NotesList");
driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
往下我们就跟踪下创建session在routing路由表里对应的controller是怎么实现和bootstrap的通信的,但是其实在真正实现通信之前,appium需要先去初始化一个async库的queue队列来排队我们需要发送到bootstrap的命令任务,我们下面会一步步看这个队列是怎么建立起来的。
我们先找到routing中对应的controller方法:
rest.post('/wd/hub/session', controller.createSession);
处理函数是controller的createSession这个方法,我们进去看看:
exports.createSession = function (req, res) {
if (typeof req.body === 'string') {
req.body = JSON.parse(req.body);
}
...
req.appium.start(req.body.desiredCapabilities, function (err, instance) { ...
}
它会先取得http client发过来的request的body,也就是上面包含我们的capabilities的那一串键值对组成的字符串了。然后将这些键值对转换成JSON格式,最后就以这些capabilities作为参数来调用req.appium的start方法,还记得req.appium是在哪里赋值的吗?对,就在上面初始化routing的时候调用的‘controller.getGlobalBeforeFilter“这个方法里面了,初始化成我们在启动http服务器时创建的那个appium server了(如果不清楚appium server是在启动http服务器过程中什么时候创建的,请查看上一篇文章)。好我们跳进该方法继续往下看:
Appium.prototype.start = function (desiredCaps, cb) { var configureAndStart = function () { this.desiredCapabilities = new Capabilities(desiredCaps); this.updateResetArgsFromCaps(); this.args.webSocket = this.webSocket; // allow to persist over many sessions this.configure(this.args, this.desiredCapabilities, function (err) { if (err) { logger.debug("Got configuration error, not starting session"); this.cleanupSession(); cb(err, null); } else { this.invoke(cb); } }.bind(this)); }.bind(this); if (this.sessionId === null) { configureAndStart(); } else if (this.sessionOverride) { logger.info("Found an existing session to clobber, shutting it down " + "first..."); this.stop(function (err) { if (err) return cb(err); logger.info("Old session shut down OK, proceeding to new session"); configureAndStart(); }); } else { return cb(new Error("Requested a new session but one was in progress")); } }; |
代码开始就是些根据传进来的capabilites参数初始化一个Capabilities对象之类的,这里Capabilities这个类值得一提的地方是它定义了一系列的capability,其中有一类是我们在测试脚本中必须填写的:
var requiredCaps = [
'platformName'
, 'deviceName'
];
也就是说其他的capability我们在脚本中可以根据需求取配置填写,但是这两个是必须的,硬性要求的。其实根据我对现有源码的研究,在安卓上面只有platformName是必须的,deviceName只有在ios上面才会用到,只是为了保持一致性,测试安卓时还是需要传进来而已,但是无论你设置什么值都没有影响。
好,我们继续往下看,Appium类的start方法在实例化好Capabilities类后,往下有几步非常重要:
第一步:通过调用configure方法来初始化Android设备类,Android设备类的实例维护的Appium Work Queue
第二步:通过调用invoke方法建立好uiautomator类与bootstrap的连接
Appium.prototype.configure = function (args, desiredCaps, cb) {
var deviceType;
try {
deviceType = this.getDeviceType(args, desiredCaps);
...
}
...
this.device = this.getNewDevice(deviceType);
this.device.configure(args, desiredCaps, cb);
...
};