CommandScheduler本身也是一个线程,是在Console线程启动时启动的,作为命令调度的线程,主要作用就是检查本身的CommandQueue中是不是有需要处理的command,进行调度不至于多命令或者多设备时出现混乱,以及启动真正的命令执行线程。
public void run() {
assertStarted();
try {
IDeviceManager manager = getDeviceManager();
startRemoteManager();
// 在启动的第一时间先countDown,此时main线程就会被释放,就退出了
mRunLatch.countDown();
// add a listener that will wake up scheduler when a new avail device is added
manager.addDeviceMonitor(new AvailDeviceMonitor());
// 进入了循环处理命令,直到输入了退出命令
while (!isShutdown()) {
// 每过30s起来看看是不是有需要调度的命令
mCommandProcessWait.waitAndReset(mPollTime);
checkInvocations();
processReadyCommands(manager);
postProcessReadyCommands();
}
// 退出循环之后代表程序即将退出了,进行一些清理工作
mCommandTimer.shutdown();
manager.terminateDeviceRecovery();
manager.terminateDeviceMonitor();
CLog.i("Waiting for invocation threads to complete");
waitForAllInvocationThreads();
closeRemoteClient();
if (mRemoteManager != null) {
mRemoteManager.cancelAndWait();
}
exit(manager);
cleanUp();
CLog.logAndDisplay(LogLevel.INFO, "All done");
} finally {
// Make sure that we don't quit with messages still in the buffers
System.err.flush();
System.out.flush();
}
}
在CommandScheduler的循环中去不断的看是否有新的命令,前面添加命令的时候,有notifyAll,以及上一个命令执行完毕等,当需要有命令被调度时都会有notifyAll去及时唤醒CommandScheduler开始干活。
public synchronized void waitAndReset(long maxWaitTime) {
waitForEvent(maxWaitTime);
reset();
}
// 这里是等待命令的逻辑
// 大致逻辑就是每过30s就看看是不是有需要调度的命令
public synchronized boolean waitForEvent(long maxWaitTime) {
if (maxWaitTime == 0) {
return waitForEvent();
}
long startTime = System.currentTimeMillis();
long remainingTime = maxWaitTime;
while (!mEventReceived && remainingTime > 0) {
try {
wait(remainingTime);
} catch (InterruptedException e) {
CLog.w("interrupted");
}
remainingTime = maxWaitTime - (System.currentTimeMillis() - startTime);
}
return mEventReceived;
}
调度processReadyCommands
protected void processReadyCommands(IDeviceManager manager) {
Map scheduledCommandMap = new HashMap<>();
// 命令调度是同步的,但是命令执行不是
// 也就是说在调度符合的情况下,是可以开启多个命令执行的线程的
// 即可以有多条命令同时运行
synchronized (this) {
Collections.sort(mReadyCommands, new ExecutableCommandComparator());
Iterator cmdIter = mReadyCommands.iterator();
while (cmdIter.hasNext()) {
// 遍历mReadyCommands取出命令
ExecutableCommand cmd = cmdIter.next();
IConfiguration config = cmd.getConfiguration();
// 创建命令context
IInvocationContext context = new InvocationContext();
context.setConfigurationDescriptor(config.getConfigurationDescription());
Map devices =
// 申请设备
allocateDevices(config, manager);
// 如果能申请到设备,说明当前命令是可以执行的
if (!devices.isEmpty()) {
// 从ready Queue中移除
cmdIter.remove();
// 添加到正在执行的queue
mExecutingCommands.add(cmd);
context.addAllocatedDevice(devices);
// 记录正在执行的命令的map
scheduledCommandMap.put(cmd, context);
mUnscheduledWarning.remove(cmd);
} else {
// 没有空闲的设备时提示警告
if (!mUnscheduledWarning.contains(cmd)) {
CLog.logAndDisplay(LogLevel.DEBUG, "No available device matching all the "
+ "config's requirements for cmd id %d.",
cmd.getCommandTracker().getId());
System.out.println(
String.format(
"The command %s will be rescheduled.",
Arrays.toString(cmd.getCommandTracker().getArgs())));
mUnscheduledWarning.add(cmd);
}
}
}
}
// 这里真正开始执行
for (Map.Entry cmdDeviceEntry : scheduledCommandMap
.entrySet()) {
ExecutableCommand cmd = cmdDeviceEntry.getKey();
// 开启命令执行的新线程去执行命令
startInvocation(cmdDeviceEntry.getValue(), cmd,
new FreeDeviceHandler(getDeviceManager()));
// 如果命令是循环模式,则再加入队列进行调度
if (cmd.isLoopMode()) {
addNewExecCommandToQueue(cmd.getCommandTracker());
}
}
}
开始执行命令的线程 InvocationThread
private void startInvocation(
IInvocationContext context,
ExecutableCommand cmd,
IScheduledInvocationListener... listeners) {
initInvocation();
// Check if device is not used in another invocation.
throwIfDeviceInInvocationThread(context.getDevices());
CLog.d("starting invocation for command id %d", cmd.getCommandTracker().getId());
final String invocationName = String.format("Invocation-%s",
context.getSerials().get(0));
// 初始化InvocationThread
InvocationThread invocationThread = new InvocationThread(invocationName, context, cmd,
listeners);
logInvocationStartedEvent(cmd.getCommandTracker(), context);
// 开启命令执行的线程
invocationThread.start();
addInvocationThread(invocationThread);
}
命令执行 InvocationThread.run
InvocationThread类的run方法开始执行,也就代表一个命令的真正执行就开始了,其实命令执行主要还是把前面封装好的Configuration中的组件拿出来,协作运行,主要还是执行其中最重要的test标签下的类中的模板方法,也就是说,test组件虽然是自己实现的,但是是遵循了模板接口的,命令的执行本质就是讲组件取出来协作运行,然后调用模板方法,也就走到了真正test组件的自定义要执行的方法。
在run方法中,其实做的事情不多,就是调用了初始化时创建的TestInvocation对象的invoke方法
instance.invoke(mInvocationContext, config,
new Rescheduler(mCmd.getCommandTracker()), mListeners);
组件取出运行都在TestInvocation中,这个类功能很多,承担了整个框架的大部分组件的实际运行。
命令的调度CommandScheduler的作用主要负责命令以及设备之间的调度,因为一条命令支持多设备运行,也可以多个设备同时运行不同的命令,必须有一个调度的角色去处理,CommandScheduler通过其内部的几个队列以及获取当前的设备状态完成调度,保证每条命令都能有条不紊的执行。