StateMachine总结(一)相关基础类
本篇StateMachine的总结均基于android7.0 wifi状态机。
什么是状态机?
-
StateMachine在我们Android系统中用途较广泛,包括蓝牙,wifi,telephony都有继承StateMachine实现自己的状态管理。
-
什么是状态呢?
状态是事物的一种属性,在一般情况下是指事物所表现出来的行为或形态。
例:汽车(点火、行驶、故障、停车) 水(液体、固体、气体)
-
android的状态机的的意义:
对于接受到某个事件(消息)后,不同的状态下能做出不同的操作。
-
StateMachine中几乎所有重要的方法均封装在SmHandler中,所以SmHandler才是StateMachine的核心,StateMachine只是对SmHandler的再次封装。
State
public class State implements IState {
protected State() {
}
@Override
public void enter() {
}
@Override
public void exit() {
}
@Override
public boolean processMessage(Message msg) {
return false;
}
@Override
public String getName() {
String name = getClass().getName();
int lastDollar = name.lastIndexOf('$');
return name.substring(lastDollar + 1);
}
}
1、State实现IState接口,自己写状态均要继承State类,即所有状态都是State的派生类。
2、 这是State封装的几个方法,enter()是在进入该状态时调用,常做一些准备工作。 exit()离开是调用,做善后工作。processMessage()的工作是,处于该状态时,接收到Msg做相应的处理,即前言里说到的:不同的状态下对相同的消息能做出不同的操作。一般自己写状态都会重写该方法。
StateInfo
private class StateInfo {
State state;
StateInfo parentStateInfo;
boolean active;
}
State表示该状态, parentStateInfo表示该状态的父状态, active表示该状态是否激活,enter()(进入该状态)为true, exit()(退出该状态)为false, 具体做何用,后面再看到,并详细解释。
###State与StateInfo的关系
/** The map of all of the states in the state machine */
private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();
State和StateInfo以key和values的方式存在名为mStateInfo的HashMap中,每一个State对应一个StateInfo.
SmHander
SmHandler的主要功能有以下三个:
- 建立树形层次结构存储State;
- 状态机的建立和状态切换;
- 消息处理和派发。
###建立树形层次结构存储State
状态机对个状态以树的形式管理,实现自己的状态机第一步是要将状态建立好,然后通过addState(State state,State parent)方法将状态存到mstateInfo中,下面是该方法具体的实现,并对每行代码的注释:
private final StateInfo addState(State state, State parent) {
StateInfo parentStateInfo = null;
if (parent != null) {
parentStateInfo = mStateInfo.get(parent); //取出父节点的parentStateInfo信息
if (parentStateInfo == null) { //如果父节点为null,则递归调用,把当前节点作为父节点
parentStateInfo = addState(parent, null);
}
}
StateInfo stateInfo = mStateInfo.get(state); //新节点,肯定为null
if (stateInfo == null) {
stateInfo = new StateInfo();
mStateInfo.put(state, stateInfo); //将state和stateInfo放入hashMap中,后面根据state即可取出stateInfo信息
}
if ((stateInfo.parentStateInfo != null) && (stateInfo.parentStateInfo != parentStateInfo)) { //新创建还没有赋值,肯定为null,否则抛出异常
throw new RuntimeException("state already added");
}
stateInfo.state = state;
stateInfo.parentStateInfo = parentStateInfo;//以后通过state可以找到父节点,一直递归,如果state的父节点为null,则表示到了顶部
stateInfo.active = false; //时候是当前活动状态标志
return stateInfo;
}
通过以上方法便达到建立状态树的目的,例如:
###状态机的建立和状态切换
状态树建立好之后,得设个初始状态,方法为setInitialState(State initialState), 该方法的很简单,只是将initialState赋值给mInitialState的变量。
初始状态设好后,状态机的准备工作完成,后面就是让状态机run起来,StateMachine提供了这个方法:
public void start(){
/** Send the complete construction message */
mSmHandler.completeConstruction();
}
这个方法只是调了SmHandler的completeConstruction()方法,下面是该方法的具体实现的核心部分:
private final void completeConstruction() {
int maxDepth = 0;
… …
mStateStack = new StateInfo[maxDepth];
mTempStateStack = new StateInfo[maxDepth]; //初始化这两个数组
setupInitialStateStack();
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
}
-
setupInitialStateStack()这个方法的实现看后面,这里不好讲清楚。
-
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj)), 这里发送SM_INIT_CMD的消息,这便是SmHandler的第三个功能。
消息处理和派发
状态机run起来,我们怎么通过状态机达到做事情的目的呢? 前面说State这个类时谈到重写processMessage()以对接收的的消息进行处理,比如我实现一个状态机MyStateMachine,肯定继承与StateMachine。
把MyStateMachine看做是server端,app看做是client端,client端怎么让server做事呢? – 发消息的方式。 当前状态会对消息做不同的处理。 StateMachine封装的方法:
消息的派发:
public final void sendMessage(Message msg)
{
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
smh.sendMessage(msg);
}
可以看到,仍然是通过SmHandler的sendMessage,所以,消息的接收处理也是在SmHandler的handleMessage(Message msg).
下面是handleMessage核心代码:
消息的处理:
public final void handleMessage(Message msg) {
… … …
/** State that processed the message */
State msgProcessedState = null;
if (mIsConstructionCompleted) {
msgProcessedState = processMsg(msg);
} else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
&& (mMsg.obj == mSmHandlerObj)) {
/** Initial one time path. */
mIsConstructionCompleted = true;
invokeEnterMethods(0);
} else {
throw new RuntimeException("StateMachine.handleMessage: "
+ "The start method not called, received msg: " + msg);
}
performTransitions(msgProcessedState, msg);
… … …
}
其中重要的地方有三点:
- processMsg(msg):通过这个方法调用各个状态的重写的processMessage(), 设初始状态时,这里不调用,因为mIsConstructionCompleted此时为false;
- invokeEnterMethods(0):这个方法的触发条件是mMsg.what == SM_INIT_CMD, 这个msg只在设初始状态时接收到,所以,在设初始状态时会invokeEnterMethods(0),这个方法以后在每次状态切换的时候都用上,但只有这里的参数是0。
- performTransitions():这个是很重要的方法,状态切换其实是在这个方法里实现的,具体实现核心代码看下面:
private void performTransitions(State msgProcessedState, Message msg) {
... ...
State destState = mDestState;
if (destState != null) {
while (true) { //从当前节点往上查找,将当前节点及active=false的父节点存在mTempStateStack中,直到找到active=true的父节点,并将其返回。
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
invokeExitMethods(commonStateInfo);
int stateStackEnteringIndex = moveTempStateStackToStateStack();
invokeEnterMethods(stateStackEnteringIndex);
moveDeferredMessageAtFrontOfQueue();
if (destState != mDestState) { // A new mDestState so continue looping
destState = mDestState;
} else {
break;
}
}
mDestState = null;
}
… …
}
这里调用invokeExitMethods()和invokeEnterMethods()实现状态的进入和退出,做到状态的切换。
##END