在看state machine代码(StateMachine.java)的时候,发现源码的注释写的很好; 前半部分是讲解,后半部分是示例代码,读过之后state machine的要点也就基本掌握了。今天就做下翻译。当然翻译的意义并不大,能看英文的还是直接看英文的好,这里只是给自己练练手。言归正传,原文如下:
这里定义的是一个分层次的状态机,可以处理消息,并且拥有预制的分层状态。
状态是一个State对象,必须实现processMessage方法,选择性的实现enter/exit/getName方法。enter/exit就相当于面向对象编程里的构造和析构方法,分别用于初始化和清理状态对象。getName方法可以返回状态对象的名字; 默认实现是返回类名。建议实现getName方法返回state实例的名字,特别是一个state类有多个实例的情况。
当一个state machine被创建的时候,addState方法用于创建状态的层次结构,setInitialState方法用于确定初始状态。完成构造之后可以通过调用start方法初始化,并开启这个state machine。StateMachine的第一个动作是调用所有层次状态的enter方法,并从它的根父状态开始。enter方法调用的完成是在StateMachine的handler中,而不是在start方法的调用中,并且这些enter方法的调用是在所有消息被处理之前。例如,下面例子中的mP1.enter首先被调用,然后是mS1.enter被调用。最终,发往这个state machine的消息会被当前状态处理, 即下面例子中的初始状态mS1的precessMessage方法。
mP1
/ \
mS2 mS1 --->initial state
当state machine被创建完成,并start后,消息可以通过obtainMessage方法创建,并通过sendMessage方法发往state machine。当state machine接收到消息后,当前状态的processMessage方法会被调用。 在上面的例子中,mS1.processMessage会被第一个调用。当前状态可以通过transitonTo方法转往新状态。
state machine中的每个状态可以没有或者有一个父状态。 如果一个子状态不能够处理消息,那么可以返回false 或者Not_HANDLED, 交由它的父状态处理。如果一个消息没有被任何状态处理,那么unhandledMessage方法会被调用,这是state machine处理这个消息的最后机会。
当所有的处理任务完成之后, state machine可以选择调用transitionToHaltingState方法。当 当前状态的processMessage方法返回后,state machine将会转换到Halting中间状态,并且调用onHalting方法。此后state machine收到的所有消息都会导致haltedProcessMessage方法被调用。
完全停止state machine可以调用quit或者quitNow方法。这两个方法会调用当前状态和它父状态的exit方法,然后onQutting方法会被调用,最后退出Thread/Loopers。
除了processMessage方法,每个状态还有enter和exit方法可以被重写。
由于所有的状态是分层预制的,转换到一个新状态会引起当前状态退出,并且进入新状态。靠近当前状态的通用父状态(多个状态的父状态)被用于确定应该进入/退出的状态序列。首先从当前状态退出,并依次退出父状态,直到遇到通用父状态,然后依次进入这个通用父状态下的子状态,直到进入目标状态。如果没有通用父状态,所有的状态都会退出,然后直接进入目标新状态。
状态可以调用的另外两个方法是deferMessage和sendMessageAtFrontOfQueue。sendMessageAtFrontOfQueue方法可以发送一个消息,并将它放在队列的首位,而不是末尾。deferMessage方法的调用会导致message被放进一个list中,直到转换到新状态; 等转换到新状态后所有被延迟的消息都会被放到state machine消息队列的前部。这些消息会被新状态优先处理。deferMessage方法在Android N 上还是protected类型, 在Android O上是public类型; sendMessageAtFrontOfQueue方法在Android O上依然是protected类型, 只可以在state machine内部调用。
下面的例子用一个拥有8个状态的state machine来说明了上面介绍的特点:
mP0
/ \
mP1 mS0
/ \
mS2 mS1
/ \ \
mS3 mS4 mS5 --->initial state
当开启mS5状态后,mP0, mP1, mS1和mS5都会处于活跃状态。所以当收到消息的时候,如果每个状态的processMessage方法都无法处理这个消息而返回false或则NOT_HANDLED,那么processMessage方法被调用的顺序是mS5, mS1, mP1,mP0。
现在假设mS5.processMessage接收到一个它可以处理的消息,而且在处理的过程中确定要转换到新状态。mS5.processMessage可以调用transitionTo(mS4),并且返回true或者HANDLED。当从processMessage方法返回后,state machine的runtime会立刻找到通用父状态,即mP1。然后state machine会调用mS5.exit,mS1.exit,后续调用mS2.enter和mS4.enter。现在活跃的状态是mP0, mP1,mS2和mS4。所以当收到下一个消息的时候,mS4.processMessage会被调用。
下面是HelloWorld式的具体例子, 每个消息都会打印”Hello World”。
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());
}
下面的state machine更有趣,他有4个状态,却有两个相互独立的父状态。
mP1 mP2
/ \
ms2 mS1
下面用伪代码对这个state machine做了描述。
state mP1 {
enter { log("mP1.enter"); }
exit { log("mP1.exit"); }
on msg {
CMD_2 {
send(CMD_3);
defer(msg);
transitionTo(mS2);
return HANDLED;
}
return NOT_HANDLED;
}
}
INITIAL
state mS1 parent mP1 {
enter { log("mS1.enter"); }
exit { log("mS1.exit"); }
on msg {
CMD_1 {
transitionTo(mS1);
return HANDLED;
}
return NOT_HANDLED;
}
}
state mS2 parent mP1 {
enter { log("mS2.enter"); }
exit { log("mS2.exit"); }
on msg {
CMD_2 {
send(CMD_4);
return HANDLED;
}
CMD_3 {
defer(msg);
transitionTo(mP2);
return HANDLED;
}
return NOT_HANDLED;
}
}
state mP2 {
enter {
log("mP2.enter");
send(CMD_5);
}
exit { log("mP2.exit"); }
on msg {
CMD_3, CMD_4 { return HANDLED; }
CMD_5 {
transitionTo(HaltingState);
return HANDLED;
}
return NOT_HANDLED;
}
}
下面代码是具体实现,StateMachineTest也有这部分代码:
class Hsm1 extends StateMachine {
public static final int CMD_1 = 1;
public static final int CMD_2 = 2;
public static final int CMD_3 = 3;
public static final int CMD_4 = 4;
public static final int CMD_5 = 5;
public static Hsm1 makeHsm1() {
log("makeHsm1 E");
Hsm1 sm = new Hsm1("hsm1");
sm.start();
log("makeHsm1 X");
return sm;
}
Hsm1(String name) {
super(name);
log("ctor E");
// Add states, use indentation to show hierarchy
addState(mP1);//添加状态mP1
addState(mS1, mP1);//添加状态mS1,mP1为其父状态
addState(mS2, mP1);//添加状态mS2,mP1为其父状态
addState(mP2);//添加状态mP2
// Set the initial state
setInitialState(mS1);//将mS1设置为初始状态
log("ctor X");
}
class P1 extends State {//状态类P1
@Override public void enter() {
log("mP1.enter");
}
@Override public boolean processMessage(Message message) {
boolean retVal;
log("mP1.processMessage what=" + message.what);
switch(message.what) {
case CMD_2:
// CMD_2 will arrive in mS2 before CMD_3
sendMessage(obtainMessage(CMD_3));//发送消息CMD_3
deferMessage(message);//延迟CMD_2,转换到状态mS2处理
transitionTo(mS2);//转换到mS2状态
retVal = HANDLED;
break;
default:
// Any message we don't understand in this state invokes unhandledMessage
retVal = NOT_HANDLED;
break;
}
return retVal;
}
@Override public void exit() {
log("mP1.exit");
}
}
class S1 extends State {//状态类S1
@Override public void enter() {
log("mS1.enter");
}
@Override public boolean processMessage(Message message) {
log("S1.processMessage what=" + message.what);
if (message.what == CMD_1) {//收到消息CMD_1,就转换到mS1状态,这有意思,退出再进来一次
// Transition to ourself to show that enter/exit is called
transitionTo(mS1);//转换到mS1,重新进mS1
return HANDLED;
} else {//否则不处理,返回NOT_HANDLED
// Let parent process all other messages
return NOT_HANDLED;
}
}
@Override public void exit() {
log("mS1.exit");
}
}
class S2 extends State {//状态类S2
@Override public void enter() {
log("mS2.enter");
}
@Override public boolean processMessage(Message message) {
boolean retVal;
log("mS2.processMessage what=" + message.what);
switch(message.what) {
case(CMD_2)://收到CMD_2消息后发送CMD_4消息
sendMessage(obtainMessage(CMD_4));
retVal = HANDLED;
break;
case(CMD_3)://收到CMD_3消息后,延迟该消息,转换到状态mP2后处理
deferMessage(message);
transitionTo(mP2);
retVal = HANDLED;
break;
default:
retVal = NOT_HANDLED;
break;
}
return retVal;
}
@Override public void exit() {
log("mS2.exit");
}
}
class P2 extends State {//状态类P2
@Override public void enter() {
log("mP2.enter");
sendMessage(obtainMessage(CMD_5));//进入该状态就发送CMD_5消息
}
@Override public boolean processMessage(Message message) {
log("P2.processMessage what=" + message.what);
switch(message.what) {
case(CMD_3):
break;
case(CMD_4):
break;
case(CMD_5)://收到CMD_5消息后转换到Halting状态
transitionToHaltingState();
break;
}
return HANDLED;
}
@Override public void exit() {
log("mP2.exit");
}
}
@Override
void onHalting() {
log("halting");
synchronized (this) {
this.notifyAll();//唤醒wait的线程
}
}
//创建各个状态类的实例对象
P1 mP1 = new P1();
S1 mS1 = new S1();
S2 mS2 = new S2();
P2 mP2 = new P2();
}
如果在执行的过程中只发送CMD_1和CMD_2这两个消息(由于使用了hsm.wait(), 所以需要使用synchronize同步块,并且基于hsm):
Hsm1 hsm = makeHsm1();//构造并启动状态机
synchronize(hsm) {
hsm.sendMessage(obtainMessage(hsm.CMD_1));//发送CMD_1消息
hsm.sendMessage(obtainMessage(hsm.CMD_2));//发送CMD_2消息
try {
// wait for the messages to be handled
hsm.wait();
} catch (InterruptedException e) {
loge("exception while waiting " + e.getMessage());
}
}
输出如下:
D/hsm1 ( 1999): makeHsm1 E //makeHsm1()最先被调用,这个log第一个打印,即开始构造状态机
D/hsm1 ( 1999): ctor E //进入状态机Hsm1的构造函数
D/hsm1 ( 1999): ctor X //退出状态机Hsm1的构造函数
D/hsm1 ( 1999): mP1.enter //Start状态机后,mS1作为初始状态,会引起其父状态enter方法被调用
D/hsm1 ( 1999): mS1.enter //mS1.enter被调用
D/hsm1 ( 1999): makeHsm1 X //状态机构造结束
D/hsm1 ( 1999): mS1.processMessage what=1 //状态mS1收到CMD_1消息,重新进入mS1状态
D/hsm1 ( 1999): mS1.exit //mS1.exit方法被调用
D/hsm1 ( 1999): mS1.enter //mS1.enter方法被调用。
D/hsm1 ( 1999): mS1.processMessage what=2 //mS1收到CMD_2消息,但是不处理,交由父状态mP1处理
D/hsm1 ( 1999): mP1.processMessage what=2 //mP1收到CMD_2消息;发送CMD_3,延迟CMD_2,转换到mS2
D/hsm1 ( 1999): mS1.exit //退出mS1状态,mS1.exit被调用
D/hsm1 ( 1999): mS2.enter //进入mS2状态,mS2.enter被调用
D/hsm1 ( 1999): mS2.processMessage what=2 //mS2优先处理CMD_2消息,发送CMD_4(队列中的排在CMD_3之后)
D/hsm1 ( 1999): mS2.processMessage what=3 //mS2收到CMD_3消息; 延迟CMD_3,并转换到mP2状态
D/hsm1 ( 1999): mS2.exit //退出mS2状态, mS2.exit方法被调用
D/hsm1 ( 1999): mP1.exit //退出mP1状态,mP1.exit方法被调用
D/hsm1 ( 1999): mP2.enter //进入mP2状态,mP2.enter方法被调用, 并发生CMD_5(队列中排在CMD_4之后)
D/hsm1 ( 1999): mP2.processMessage what=3 //mP2优先处理之前被延迟的CMD_3消息
D/hsm1 ( 1999): mP2.processMessage what=4 //处理CMD_4消息
D/hsm1 ( 1999): mP2.processMessage what=5 //处理CMD_5消息; 转入Halting状态
D/hsm1 ( 1999): mP2.exit //退出mP2状态,mP2.exit方法被调用。
D/hsm1 ( 1999): halting //onHalting方法被调用,在该方法中notifyAll会唤醒阻塞的线程。