[上一篇文章](http://testerhome.com/topics/1858)已经讲了cts如何自动检测到设备,效果就是无需我们再去调用`ADB`的`getIDevice()`得到设备,利用的是ADB中提供的观察者模式做到了这一点,那么得到设备后我们如何对这些设备进行管理的呢?
# 设备分类
cts中将设备分为3种状态:处于验证中的设备,可用设备,执行任务的设备。这三种状态的设备分别用3个集合保存:
~~~
//处于验证中的设备集合
private Map<String, IDeviceStateMonitor> mCheckDeviceMap;
//可用设备的集合
private ConditionPriorityBlockingQueue<IDevice> mAvailableDeviceQueue;
//执行任务的设备集合
private Map<String, IManagedTestDevice> mAllocatedDeviceMap;
~~~
## 1.处于验证中的设备集合
`mCheckDeviceMap`是一个`Map`,`key`值表示设备的`SN`号,`value`值表示当前设备的状态监听器对象`IDeviceStateMonitor`(以后会讲到)。当一个新设备被检测的时候会首先放到该Map中,然后会调用`IDeviceStateMonitor`.`waitForDeviceShell(final long waitTime)`来对设备进行一个扫描,扫描通过以后,就会将设备添加到可用设备集合`mAvailableDeviceQueue`中。但是不管扫描成不成功,经过检测步骤后,都会从`mCheckDeviceMap`设备集合中删除该设备。所以可以说该容器这是临时存放设备用的,为的是对设备进行验证是否可用,就像机场里过安检一样。
## 2.可用的设备集合
`mAvailableDeviceQueue`是一个[优先队列](http://baike.baidu.com/view/1267829.htm),且是线程安全的,这是`cts`自定义的一个队列数据结构。你可以传入条件选择设备,比如按设备号选择,按平台号选择都可以。返回的是一个`IDevice`对象,这是原生的`ADB`中定义的接口类。该集合是一个中间集合,它从`mCheckDeviceMap`中得到集合,然后等到用户使用设备后,就将集合中的某个元素"送给"了执行任务的设备集合`mAllocatedDeviceMap`。
## 3.执行任务的设备集合
`mAllocatedDeviceMap`集合是一个Map,`key`值表示设备的`SN`号,`value`值表示的是cts自己定义的设备对象的接口`IManagedTestDevice`。当用户选择一个可用设备后,是从可用设备集合`mAvailableDeviceQueue`中得到了一个`IDevice`,然后`cts`使用外观模式,将其封装到了继承自`IManagedTestDevice`接口的对象`TestDevice`(这个类很重要,以后会单独讲)中,里面有很多关于设备的方法可以被调用,那么用户实际能操作就是这个`TestDevice`类。等待任务完成后,该设备将“送还”到可用设备集合中,这只是一个借用的过程,用完了就还给了它,这样的话这个设备还可以继续执行其他任务。
## 4.总结
通过上面的介绍,我们用一幅图来描述一下3个集合之间的关系。
![](https://box.kancloud.cn/2016-01-09_56911ddf21518.jpg)
# 检测设备后分类
终于到了揭晓庐山真面目的时候,以上的种种解释,包括第一篇文章的铺垫,都是为了下面的内容铺垫的,讲代码的东西就是这么的麻烦。先上代码:
~~~
private class ManagedDeviceListener implements IDeviceChangeListener {
/**
* {@inheritDoc}
*/
@Override
public void deviceChanged(IDevice device, int changeMask) {
IManagedTestDevice testDevice = mAllocatedDeviceMap.get(device.getSerialNumber());
if ((changeMask & IDevice.CHANGE_STATE) != 0) {
if (testDevice != null) {
TestDeviceState newState = TestDeviceState.getStateByDdms(device.getState());
testDevice.setDeviceState(newState);
} else if (mCheckDeviceMap.containsKey(device.getSerialNumber())) {
IDeviceStateMonitor monitor = mCheckDeviceMap.get(device.getSerialNumber());
monitor.setState(TestDeviceState.getStateByDdms(device.getState()));
} else if (!mAvailableDeviceQueue.contains(device) && device.getState() ==IDevice.DeviceState.ONLINE) {
checkAndAddAvailableDevice(device);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void deviceConnected(IDevice device) {
CLog.d("Detected device connect %s, id %d", device.getSerialNumber(), device.hashCode());
IManagedTestDevice testDevice = mAllocatedDeviceMap.get(device.getSerialNumber());
if (testDevice == null) {
if (isValidDeviceSerial(device.getSerialNumber()) && device.getState() == IDevice.DeviceState.ONLINE) {
checkAndAddAvailableDevice(device);
} else if (mCheckDeviceMap.containsKey(device.getSerialNumber())) {
IDeviceStateMonitor monitor = mCheckDeviceMap.get(device.getSerialNumber());
monitor.setState(TestDeviceState.getStateByDdms(device.getState()));
}
} else {
// this device is known already. However DDMS will allocate a
// new IDevice, so need
// to update the TestDevice record with the new device
CLog.d("Updating IDevice for device %s", device.getSerialNumber());
testDevice.setIDevice(device);
TestDeviceState newState = TestDeviceState.getStateByDdms(device.getState());
testDevice.setDeviceState(newState);
}
}
private boolean isValidDeviceSerial(String serial) {
return serial.length() > 1 && !serial.contains("?");
}
/**
* {@inheritDoc}
*/
@Override
public void deviceDisconnected(IDevice disconnectedDevice) {
if (mAvailableDeviceQueue.remove(disconnectedDevice)) {
CLog.i("Removed disconnected device %s from available queue",disconnectedDevice.getSerialNumber());
}
IManagedTestDevice testDevice = mAllocatedDeviceMap.get(disconnectedDevice.getSerialNumber());
if (testDevice != null) {
testDevice.setDeviceState(TestDeviceState.NOT_AVAILABLE);
} else if (mCheckDeviceMap.containsKey(disconnectedDevice.getSerialNumber())) {
IDeviceStateMonitor monitor = mCheckDeviceMap.get(disconnectedDevice.getSerialNumber());
monitor.setState(TestDeviceState.NOT_AVAILABLE);
}
updateDeviceMonitor();
}
}
~~~
上面的代码我在第一篇文章中有涉及,只是那个时候我把三个监听方法里的具体实现给隐藏了,现在终于把它展现出来了。
## deviceChanged方法
该方法会在设备连接的时候调用,但是我们做了个判断,就是设备状态的改变,那就说明该设备连接前`ADB`已经知道了该设备的存在,只是它与之前的状态发生了变化,所以在设备第一次连接的时候,该方法里的代码块是不会被调用的。那么再具体说说操作步骤:
`1`.首先判断设备的改变是否是状态的改变,因为设备的变化很有多种,我们需要关心的设备于PC连接状态的改变,所以需要做判断。
`2`.然后从`mAllocatedDeviceMap`集合中尝试获得发生改变的设备,这一步是为了检查是否会影响到执行任务的设备。因为在任务执行过程中,不是每时每刻都能去检测状态,所以cts采用的是被动判断,如果状态发生改变,需要通知正在执行任务的设备监听器,由监听器去做相应的处理。
`3`.如果确定状态发生改变的设备是正在执行任务的设备,就需要将其状态设置为新状态。
`4`.如果不是正在执行任务的设备,那么再去验证是否属于另外2个集合中的设备。
`5`.如果是检测集合`mCheckDeviceMap`中的元素,那么也要重新设置设备状态,不过这个时候是从检测设备集合中得到设备再改变它的状态。
`6`.如果改变的设备既不是执行任务的设备,也不是检测中的设备,这个时候我们要判断是否是新设备,这个时候可能有人会有疑问?为什么不判断是否存在于可分配设备中呢?这得从`deviceChanged`的调用机制来说,`deviceChanged`只在设备连接进来的时候会调用,设备掉线的时候该方法不会被调用,那么自然这个里面无需判断是否是可分配设备中。只需要判断传进来的`IDevice`的状态是否在线且不在可分配设备列表中,这个时候就要按新设备来处理啦。另外判断是否在该集合中是需要在`deviceDisconnected`中判断的。
## deviceConnected方法
这个方法会在有设备连接的时候调用,不管是新设备还是旧设备。它的处理步骤如下:
`1`.首先判断该设备是否正在执行任务,如果正在执行任务,`ok`!我们需要更新该设备的状态。如果不是进入第二步:
`2`.如果在线且通过判断其`SN`号是可用的,如果条件不通过,跳到第三步,如果通过的话,这个时候需要进行操作判断是否可以放到可用设备集合中。
`3`.是否存在于检测设备集合中,如果存在就将其设备更新下。
## deviceDisconnected方法
这个方法会在设备离线的时候调用,
`1`.首先试着从可用集合中删除该设备,然后进入第二步。
`2`.剩下的处理方式和上面的`deviceConnected`一样。
# 总结
设备管理的复杂性较之我所讲的,我所能说的也只是皮毛,希望对此有兴趣研究研究源码。提示一下,在研究源码之前先了解一下23种设计模式中常用模式,对你的理解会很有帮助。
- 前言
- (1)-windows下cts配置
- (2)-cts调试环境的搭建
- (3)-基础库tradefederation配置
- (4)-任务的添加
- (5)-9大组件配置
- (6)-任务的执行
- (7)-任务执行的调度室
- (8)-IBuildProvider
- (9)-IDeviceRecovery
- (10)-TestDeviceOptions
- (11)-ICommandOptions
- (12)-ITargetPreparer
- (13)-任务执行过程
- (14)-任务执行过程
- (15)-任务执行完
- (16)-logcat信息收集系统
- (17)-fastboot状态监听器
- (18)-设备恢复
- (19)-设备状态的分类以及恢复模式的分类
- (20)-cts自身log系统
- (21)-测试结果收集系统
- (22)-自动检测设备
- (23)-设备分类
- (24)-case的组织