实战StateMachine状态机
前面我们讲解了设计模式中的状态模式,以及分析了安卓源码中的StateMachine状态机的内部实现原理。其原理简而言之:就是通过状态树维持各种状态实例,各个状态实例相邻之间可以互相转换,不相邻的便通过内部状态树遍历查找。通过消息传递机制,发送消息并转换状态实例,从而实现对应状态下响应对应状态下应有的逻辑。
状态机的实现步骤
- 自定义StateMachine类继承StateMachine
- 自定义状态State继承State:重写enter、processMsg、exit
- 设置更新状态常量,用于状态更新
- addState加入父状态、子状态,setInitialState初始化状态
- start启动状态机stateMachine
- 使用状态机,发送持有更新状态常量的Msg,处理对应的pressageMessage
示例代码
- 我们以电梯运行为例实现状态机的实现过程
状态分析:
待机 停机 等待选择(上楼or下楼) 运行中 到站
自定义LiftStateMachine继承StateMachine
public class LiftStateMachine extends StateMachine { private static LiftStateMachine liftStateMachine; public static final int MSG_ON = 1; public static final int MSG_BUSY_UP = 2; public static final int MSG_BUSY_DOWN = 3; public static final int MSG_OVER = 4; public static final int MSG_OFF = 5; public static final int MSG_WAIT = 6; protected LiftStateMachine(String name) { super(name); initLiftStateMachine(); } protected LiftStateMachine(String name, Looper looper) { super(name, looper); initLiftStateMachine(); } protected LiftStateMachine(String name, Handler handler) { super(name, handler); initLiftStateMachine(); } private State liftOnState = new LiftOnState(); private State liftWaitState = new LiftWaitForUpOrDownState(); private State liftBusyState = new LiftBusyState(); private State liftOverState = new LiftOverState(); private State liftOffState = new LiftOffState(); private void initLiftStateMachine() { //添加状态 addState(liftOnState); addState(liftWaitState); addState(liftBusyState); addState(liftOverState); addState(liftOffState); //调用父类方法设置初始状态 setInitialState(liftOnState); //开启状态机 start(); } //创建状态机 public static StateMachine makeLiftStateMachine(){ liftStateMachine = new LiftStateMachine("LiftStateMachine"); return liftStateMachine; } }
上述代码中首先定义几种状态响应的常量,用于在各种State中分辨并响应对应的handleMessage消息。其次是实例出电梯的几种状态实例,并在初始化中加入状态树,之后设置原始状态,最后开启状态机进行工作。
电梯中各种状态的具体
2.1 待机状态 static class LiftOnState extends State { @Override public void enter() { super.enter(); Log.d(TAG, "LiftOnState --> enter"); Message message = liftStateMachine.obtainMessage(); message.what = MSG_WAIT; liftStateMachine.sendMessage(message); } @Override public boolean processMessage(Message msg) { if (msg.what == MSG_WAIT){ liftStateMachine.deferMessage(msg); liftStateMachine.transitionTo(liftStateMachine.liftWaitState); } return super.processMessage(msg); } @Override public void exit() { super.exit(); Log.d(TAG, "LiftOnState --> exit"); } }
待机状态中,在进入状态中便发送一个MSG_WAIT消息,由于当前没有切换状态实例,只有在自己的processMessage方法中处理,之后在自己的消息处理逻辑中转移消息,并切换状态实例到等待状态,此时对应电梯门前有人按下电梯按钮,需要等待电梯到达当前楼层。
2.2 等待状态 static class LiftWaitForUpOrDownState extends State { @Override public void enter() { super.enter(); Log.d(TAG, "LiftWaitForUpOrDownState --> enter"); } @Override public boolean processMessage(Message msg) { switch (msg.what){ case MSG_WAIT: Log.d(TAG, "LiftWaitForUpOrDownState --> processMessage"); Message msg1 = liftStateMachine.obtainMessage(); msg1.what = MSG_BUSY_UP; liftStateMachine.sendMessage(msg1); //延迟下楼消息 Message message = liftStateMachine.obtainMessage(); message.what = MSG_BUSY_DOWN; liftStateMachine.sendMessage(message); liftStateMachine.transitionTo(liftStateMachine.liftBusyState); break; }
return super.processMessage(msg);
}
@Override
public void exit() {
super.exit();
Log.d(TAG, "LiftWaitForUpOrDownState --> exit");
}
}
等待状态逻辑也很简单,主要接收处理MSG_WAIT消息。通过切换状态,进入等待状态的enter方法,之后响应发来的MSG_WAIT消息。此时电梯到达目标楼层,开门等待人员进入并关闭,通过代码可以看出,电梯先向上运行,再向下运行,这时候电梯就要转换状态进入运行状态,此时不能再进行开门和关门等操作。
2.3 电梯运行状态
static class LiftBusyState extends State {
@Override
public void enter() {
super.enter();
Log.d(TAG, "LiftBusyState --> enter");
}
@Override
public boolean processMessage(Message msg) {
switch (msg.what){
case MSG_BUSY_UP:
Log.d(TAG, "LiftBusyState --> MSG_BUSY_UP");
break;
case MSG_BUSY_DOWN:
Log.d(TAG, "LiftBusyState --> MSG_BUSY_DOWN");
Message m = liftStateMachine.obtainMessage();
m.what = MSG_OVER;
liftStateMachine.sendMessage(m);
liftStateMachine.transitionTo(liftStateMachine.liftOverState);
}
return super.processMessage(msg);
}
@Override
public void exit() {
super.exit();
Log.d(TAG, "LiftBusyState --> exit");
}
}
人员进入电梯,电梯便运行到达指定楼层,代码中电梯先是到达高楼层,紧接着到达低楼层后,进入到达状态,这是需要请求开门后,人员才可以出入。
2.4 到达状态
static class LiftOverState extends State {
@Override
public void enter() {
super.enter();
Log.d(TAG, "LiftOverState --> enter");
}
@Override
public boolean processMessage(Message msg) {
switch (msg.what){
case MSG_OVER:
Log.d(TAG, "LiftOverState --> MSG_OVER");
Message message = liftStateMachine.obtainMessage();
message.what = MSG_OFF;
liftStateMachine.sendMessage(message);
break;
case MSG_OFF:
Log.d(TAG, "LiftOverState --> MSG_OFF");
Log.d(TAG, "transitionTo(liftOffState), sendMessage(MSG_OFF)");
Message newMsg = liftStateMachine.obtainMessage();
newMsg.what = MSG_OFF;
liftStateMachine.sendMessage(newMsg);
liftStateMachine.transitionTo(liftStateMachine.liftOffState);
break;
}
return super.processMessage(msg);
}
Override
public void exit() {
super.exit();
Log.d(TAG, "LiftOverState --> exit");
}
}
此时电梯到达指定楼层,电梯开门,人员可以安全的出入。代码中,此种状态下响应MSG_OVER消息。但是代码中再响应MSG_OVER没有切换状态,而是在自己响应自己发出的MSG_OFF的消息。稍后我们可以查看log得出此时的MSG_OFF消息是到达状态响应还是已经跳转到停机状态,由停机状态响应。
2.5 停机状态
static class LiftOffState extends State {
@Override
public void enter() {
super.enter();
Log.d(TAG, "LiftOffState --> enter");
}
@Override
public boolean processMessage(Message msg) {
switch (msg.what){
case MSG_OFF:
Log.d(TAG, "LiftOffState --> MSG_OFF");
break;
default:
break;
}
return super.processMessage(msg);
}
@Override
public void exit() {
super.exit();
Log.d(TAG, "LiftOffState --> exit");
}
}
电梯停机状态逻辑更是简单,对传来的MSG_OFF关机消息,简单的打印了一个log。当然可以在这里响应其他楼层的电梯等待行为,需要接收MSG_WAIT消息,并将当前状态切换为等待状态,之后电梯才能知道自己要进入运行状态到达指定楼层去接人。
log分析总结
通过控制台打印的log进行总结
//电梯开机,便发送消息进入WAIT状态 LiftState: LiftOnState --> enter LiftState: LiftOnState --> exit //WAIT状态下等待人员进入,之后电梯关门进入运行状态 LiftWaitForUpOrDownState --> enter LiftWaitForUpOrDownState --> processMessage LiftWaitForUpOrDownState --> exit //电梯运行状态,先上后下,最后到达指定楼层,进入到达状态 LiftBusyState --> enter LiftBusyState --> MSG_BUSY_UP LiftBusyState --> MSG_BUSY_DOWN LiftBusyState --> exit //到达指定楼层,开门等待人员出入 LiftOverState --> enter LiftOverState --> MSG_OVER //这里可以看出,状态没有改变,MSG_OFF仍然在到达状态下响应的,需要切换到停机状态才能真正实现电梯停机 LiftOverState --> MSG_OFF transitionTo(liftOffState), sendMessage(MSG_OFF) LiftOverState --> exit //电梯真正停机,没法响应其他的操作,此时逻辑上只能响应开即进入待机状态,无法进入等待,运行,达到等其他状态 LiftOffState --> enter LiftOffState --> MSG_OFF
总结
- 通过安卓源码的状态机机制,我们可以方便快捷的设计自己的状态模式模型
- 状态模式的精髓就是各个状态下,各自响应自己能够操作的逻辑。对于越界操作,需要转移状态交予正确的状态来处理。
- 维护状态的状态树原型也是一种跨越性的思维,其约束了各种状态间之间莫名的转换关系