在看蓝牙源码的时候,发现蓝牙的连接状态以及绑定状态是通过StateMachine(状态机)
来实现的。通过StateMachine
来管理不同状态下的行为动作,提高灵活性。除了蓝牙,wifi、wps同样是通过StateMachine
来处理状态。
先举两个个例子来看看StateMachine
是如何使用的,例子是从源码注释里头直接copy来的。在StateMachine
源码中可以看到它被@Hide
了,所以开发中,我们是用不了。
- 例1
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 void enter() {
super.enter();
log("State1 enter");
}
@Override
public void exit() {
super.exit();
log("State1 exit");
}
Override
public boolean processMessage(Message message) {
log("Hello World");
return HANDLED;
}
}
State1 mState1 = new State1();
}
// 测试代码
void testHelloWorld() {
HelloWorld hw = makeHelloWorld();
hw.sendMessage(hw.obtainMessage());
}
执行结果:
State1 enter
Hello World
从这个例子中可以了解到StateMachine
的使用流程分三步走:
1. `add(state)` 添加状态,将所有的状态都add进去;
2. `setInitialState(state)` 设置初始状态
3. `start()` 启动状态机
从日志上可以看出,状态机启动之后,初始状态的enter()
方法会被执行,然后往状态机里头发送message,初始状态的processMessage(msg)
方法会被执行。由于没有切换到其他状态,所以exit()
方法未被执行。
- 例2
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);
addState(mS1, mP1);
addState(mS2, mP1);
addState(mP2);
// Set the initial state
setInitialState(mS1);
log("ctor X");
}
class P1 extends State {
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));
deferMessage(message);
transitionTo(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 {
Override
public void enter() {
log("mS1.enter");
}
Override
public boolean processMessage(Message message) {
log("S1.processMessage what=" + message.what);
if (message.what == CMD_1) {
// Transition to ourself to show that enter/exit is called
transitionTo(mS1);
return HANDLED;
} else {
// Let parent process all other messages
return NOT_HANDLED;
}
}
Override
public void exit() {
log("mS1.exit");
}
}
class S2 extends State {
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):
sendMessage(obtainMessage(CMD_4));
retVal = HANDLED;
break;
case(CMD_3):
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 {
Override
public void enter() {
log("mP2.enter");
sendMessage(obtainMessage(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):
transitionToHaltingState();
break;
}
return HANDLED;
}
Override
public void exit() {
log("mP2.exit");
}
}
Override
void onHalting() {
log("halting");
synchronized (this) {
this.notifyAll();
}
}
P1 mP1 = new P1();
S1 mS1 = new S1();
S2 mS2 = new S2();
P2 mP2 = new P2();
}
// 测试代码
Hsm1 hsm = makeHsm1();
synchronize(hsm) {
hsm.sendMessage(obtainMessage(hsm.CMD_1));
hsm.sendMessage(obtainMessage(hsm.CMD_2));
try {
// wait for the messages to be handled
hsm.wait();
} catch (InterruptedException e) {
loge("exception while waiting " + e.getMessage());
}
}
这个例子中有4个状态,5条命令,多次的状态切换。
-
step1: 构建状态机并添加状态,添加完状态之后的状态树如下:
mP1 mP2 / \ 初始值->mS1 mS2
其中mP1是mS1和mS2的父状态。
-
step2: 调用
start()
方法,启动状态机,此时会打印如下日志:makeHsm1 E ctor E ctor X mP1.enter mS1.enter makeHsm1 X
虽然设置的初始状态是mS1,但是mP1的
enter()
方法也被调用,具体原因会在原理分析中说明。 -
step3: 发送
CMD_1
指令,当前状态是S1,并且S1会处理该指令事件,通过方法transitionTo(mS1)
将状态重新切换到S1,此时会打印的日志如下:mS1.processMessage what=1 mS1.exit mS1.enter
调用
transitionTo()
方法的时候,上一个状态会先调用exit()
方法,然后新的状态调用enter()
方法:oldState.exit() -> newState.enter()
。 -
step4: 发送
CMD_2
指令,此时的日志如下:mS1.processMessage what=2 mP1.processMessage what=2
S1中是不处理
CMD_2
的,但是它的父状态会处理,那么就交给P1去处理,具体原因后面分析。 -
step5:P1接收到
CMD_2
指令之后,发送CMD_3
指令,并且通过deferMessage(CMD_2)
方法将CMD_2
放到消息队列的顶端,然后切换到S2状态,日志如下:mS1.exit mS2.enter mS2.processMessage what=2 mS2.processMessage what=3
上个状态是S1,切换到S2,所以S1
exit()
, S2enter()
,由于S1和S2是父状态都是P1,那么P1维持不变。 -
step6:S2依次接收到
CMD_2
、CMD_3
指令,先后执行发送CMD_4
指令、defermessage(CMD_3)
、切换到P2状态,日志如下:mS2.exit mP1.exit mP2.enter mP2.processMessage what=3 mP2.processMessage what=4
S2切换到P2,S2先
exit()
,然后它父状态P1exit()
,最后P2执行enter()
,接着陆续处理CMD_3
、CMD_4
事件。 -
step6:P2在执行
enter()
方法的时候,发送了CMD_5
指令,CMD_5
指令是停止状态机,那么就将执行P2的exit()
。日志如下:mP2.exit halting
StateMachine
使用总结
- 在状态机的构造方法里头,通过
addState()
方法构建状态树; - 通过
setInitialState()
设置初始状态; - 通过
start()
方法启动状态机,初始状态执行enter()
方法; -
sendMessage(msg)
给状态机发送消息之后,当前状态会执行processMessage(msg)
,来处理消息,如果当前状态处理不了,则交给父状态处理,如果都处理不了,交给状态机处理; - 通过
transitionTo(state)
来切换状态,oldState执行exit()
,newState执行enter()
; -
deferMessage(msg)
暂时将当前消息保存到消息队列的顶端, 一旦切换到新的状态,首先处理该消息; - 切换到某个状态的时候,先会从当前状态开始往根状态依次执行各状态的
exit()
方法,直到与新状态相同的父状态Px为止。然后依次执行从Px到新状态路径下各状态的enter()
方法(需要注意的是,公共父状态的enter和exit方法不会执行)。