定义了一套分层处理消息的状态机,具有按层次排列的状态。
State对象必须实现processMessage方法,可以按需实现enter/exit/getName。enter/exit方法可等价于面向对象编码中的构造和销毁,会用做状态的初始化和回收。getName方法返回了状态的名称,方法的默认实现返回的是类的名称。让getName方法返回状态实例的名称可能更满足要求,特别是在一个状态类中有多个状态实例。
当一个状态记被创建,addState方法用来建造这个层次,setInitialState方法识别这些状态中的初始状态。构造完以后通过start方法初始化和启动状态机。状态机的第一个动作是递归调用初始状态的enter,从初始状态的最大父节点开始。enter是在状态机的handler中被完成,而不是在调用调用enter的上下文中,并且在任何消息被处理之前被调用。举个简单的例子:
mP1
/ \
mS2 mS1 ---initial state
mP1的enter首先调用,然后是mS1的enter;最终发送到状态机的消息被当前状态处理,在这个例子中就是mS1.processMessage。
状态记被创建和启动以后,通过obtainMessage方法创建消息,通过使用sendMessage发给状态机。当状态机收到消息,当前状态的processMessage被调用。在上面的例子中,首先mS1.processMessage被调用,然后通过transition改变到新的状态。
状态机中的每个状态都可能有0个或1个父节点,如果一个子节点状态返回false/NOT_HANDLED不能处理消息,可以让父节点状态处理这个消息。如果一个消息在子节点状态和任意祖先节点状态都没有被处理,unhandledMessage被调用,作为状态机处理消息的最后一次机会。
当所有流程都处理完成时,状态机可以选择调用transitionToHaltingState。当前processingMessage返回时,状态机变换到内部的HaltingState,并且调用halting方法。以后状态机收到的任何消息都会触发haltedProcessMessage被调用。
如果要求完全是停止状态机,调用quit/quitNow方法,这些都会调用当前状态及其祖先的exit方法,调用onQuiiting, 然后退出线程/消息循环队列。
除了processMessage方法外, 每个状态还有enter方法和exit方法可以被重写。
既然这些状态是按层次排序的,变换到一个新的状态会出发当前状态的推出和新状态的进入。为了确定进入/推出的状态列表,与当前状态最相近的共同父节点会被发现,然后从当前状态及其向上的祖先退出,但不包含共同父节点状态,然后进入共同父节点之下的状态,直到目标状态。如果没有共同父节点状态,那么所有的状态都退出,然后新状态被进入。
状态可以使用其他两个方法是deferMessage和sendMessageAtFrontQueue。sendMessageAtFrontQueue发送一条消息放到队列的头部,而不是尾部。这个deferMessage方法触发消息保存在一个列表中,直到一次新状态转换的发生。此时,所有被推迟的消息都被放到状态机队列的前面,这些消息中最旧的消息放到最前面。这些消息就会被新的状态在队列消息之前被处理。这两种都是收到保护的,只会在状态机内部被调用。
为了示例一些属性,使用8个状态层次的状态机:
mP0
/ \mS0
mP1
/ \
mS2 mS1
/ \ \
mS3 mS4 mS5 ---inittial state
启动mS5之后,激活的状态列表是mP0, mP1, mS1, mS5。因此当收到一条消息时,调用processMessage的顺序是mS5, mS1, mP1, mP0,假设每个processMessage都返回false或者NOT_HANDLED,表明不能处理这些消息。
现在假设mS5收到了一条消息,并且自己可以处理,在处理过程中确认需要变换状态。可以调用transitionTo(mS4),然后返回true/HANDLED。从processMessage返回后,状态机立刻就找共同的父节点状态mP1,之后调用mS5.exit, mS1.exit, mS2.enter, mS4.enter。新激活状态列表是mP0, mP1, mS2, mS4。因此当下一条消息被接收到mS4.processMessage就会被调用。
介绍几个更具体的例子,下面是一个经典的HelloWorld状态机,每一条消息都会回应一行“helloWorld”打印。
class HelloWorld extends StateMachine {
HelloWorld(String name) {
super(name);
addState(mState1);
setInitialState(mState1);
}
public static HelloWorld makeHelloWorld() {
HelloWorld hw = new HelloWorld("hw");
hw.start();
return hw;
}
class State1 extends State {
@Override public boolean processMessage(Message message) {
log("Hello World");
return HANDLED;
}
}
State1 mState1 = new State1();
}
void testHelloWorld() {
HelloWorld hw = makeHelloWorld();
hw.sendMessage(hw.obtainMessage());
}
下面是一个更有意思的状态机,一共有4个状态,有2个独立的父节点状态。
mP1 mP2
/ \
mS2 mS1 ---initial state
先看伪代码实现:
state mP1 {
enter {
log("mP1.enter"); }
exit {
log("mP1.exit"); }
on msg {
CMD_2 {
send(CMD_3);
defer(msg);
transitionTo(mS2);
return HANDLED