The state machine defined here is a hierarchical state machine which processes
messages and can have states arranged hierarchically.
这里的状态机是一个分层处理消息的状态机,并且是能够有分层排列状态。
1. 一个对象的行为取决于它的状态,并且它必须在运行时候根据状态来改变行为;
2. 一个操作中含有庞大的多分支条件语句,且这些分支依赖于该对象的状态。
1. 将状态封装成对象,在特定的状态下执行不同的操作,得到不同的行为模式;
2. 将状态和行为封装在一起,解决庞大分支语句带来的阅读性差和不方便扩展问题,降低程序的复杂性和提升灵活性。
public class State implements IState
{
protected State() {}
public void enter() {}
public void exit() {}
public boolean processMessage(Message msg) {}
public String getName() {}
}
状态的基类,StateMachine中的状态都是由State派生而来。
**SmHandler:**SmHandler是消息处理派发和状态控制切换的核心,运行在单独的线程上。
private static class SmHandler extends Handler
{
/** The current message */
private Message mMsg;
/** A list of messages that this state machine has processed */
private ProcessedMessages mProcessedMessages =
new ProcessedMessages();
/** Stack used to manage the current hierarchy of states */
private StateInfo mStateStack[];
/** The map of all of the states in the state machine */
private HashMap mStateInfo =
new HashMap();
/** The initial state that will process the first message */
private State mInitialState;
/** The destination state when transitionTo has been invoked */
private State mDestState;
}
SmHandler是构建StateMachine的核心,它主要包含三个功能:
1. 建立树形结构存储State;
2. 状态机的StateStack建立和状态切换;
3. 消息的处理和派发。
StateInfo:存储当前State,和其parentState,以及是否是激活状态,用来构建树形结构。
private class StateInfo
{
/** the state */
State state;
/** The parent of this state, null if there is no parent */
StateInfo parentStateInfo;
/** True when the state has been entered and on the stack */
boolean active;
}
HaltingState与QuittingState
都是State的派生类,用来处理状态停止和状态退出的情况,里面都是重写了processMessage()方法,上述两个类在StateMachine中没有实际行动,仅仅只是用来扩展和保留。
/**
* State entered when transitionToHaltingState is called.
*/
private class HaltingState extends State {
@Override
public boolean processMessage(Message msg) {
mSm.haltedProcessMessage(msg);
return true;
}
}
/**
* State entered when a valid quit message is handled.
*/
private class QuittingState extends State {
@Override
public boolean processMessage(Message msg) {
return NOT_HANDLED;
}
}
在构造一个状态机的时候需要确定有多少个状态,需要将这些状态集中起来进行管理。在StateMachine中提供了protected类型方法addState来添加状态。首先看看这个方法:
/**
* Add a new state to the state machine
* @param state the state to add
* @param parent the parent of state
*/
protected final void addState(State state, State parent) {
mSmHandler.addState(state, parent);
}
实际上,还是mSmHandler来进行操作,继续深入…
/**
* Add a new state to the state machine. Bottom up addition
* of states is allowed but the same state may only exist
* in one hierarchy.
*
* @param state the state to add
* @param parent the parent of state
* @return stateInfo for this state
*/
private final StateInfo addState(State state, State parent) {
...
StateInfo parentStateInfo = null;
if (parent != null) {
//获取当前状态parent详细信息StateInfo
parentStateInfo = mStateInfo.get(parent);
if (parentStateInfo == null) {
//当前状态父状态未加入到StateMachine中,
//递归先加入其ParentState
parentStateInfo = addState(parent, null);
}
}
//判断当前状态是否加入到StateMachine层次结构中
StateInfo stateInfo = mStateInfo.get(state);
if (stateInfo == null) {
//创建State详细信息对象,将其加入到StateMachine层次结构中
stateInfo = new StateInfo();
mStateInfo.put(state, stateInfo);
}
//验证我们没有加入相同的状态,否则异常
if ((stateInfo.parentStateInfo != null)
&& (stateInfo.parentStateInfo != parentStateInfo)) {
throw new RuntimeException("state already added");
}
//完善当前状态信息
stateInfo.state = state;
stateInfo.parentStateInfo = parentStateInfo;
stateInfo.active = false;
...
return stateInfo;
}
所以,在StateMachine类中,StateInfo就是包装State组成一个Node,建立State父子关系。
private HashMap mStateInfo = new HashMap();
接下来,举个栗子来说明下:
SmHandle sm;
sm.addState(S0,null);
sm.addState(S1,S0);
sm.addState(S2,S0);
sm.addState(S3,S1);
sm.addState(S4,S1);
sm.addState(S5,S2);
sm.addState(S6,S2);
sm.addState(S7,S2);
setInitialState(S4); //设置初始状态
该树形层次结构的构建:
1. 存储数据结构:StateInfo、HashMap<State,StateInfo> mStateInfo;
2. 构建方法:StateInfo addState(State state, State parent)。
上面栗子构建的树形层次结构如下图:
当各种状态添加到StateMachine中且各种状态初始化完毕之后,就可以启动状态机。StateMachine提供了启动状态机的方法:
public void start() {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
/** Send the complete construction message */
smh.completeConstruction();
}
smh.completeConstruction()这个方法用来构建状态机运行模型,继续跟进这个方法:
/**
* Complete the construction of the state machine.
*/
private final void completeConstruction() {
//计算State继承层次结构的最大深度以便创建运行时StateStack
int maxDepth = 0;
for (StateInfo si : mStateInfo.values()) {
int depth = 0;
for (StateInfo i = si; i != null; depth++) {
i = i.parentStateInfo;
}
if (maxDepth < depth) {
maxDepth = depth;
}
}
...
//创建StateStack
mStateStack = new StateInfo[maxDepth];
mTempStateStack = new StateInfo[maxDepth];
//根据当前mDestState按照其层次结构沿着其父子关系,
//保存此条路径上的StateInfo存储到StateStack中
//例如:S0-S2-S5 存储到mStateStack中
setupInitialStateStack();
/** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
}
提及一下,上述的StateStack是根据父子关系组成链式结构,例如S0-S2-S5;S5肯定是mDestState,而S0、S2都是其parentState,至于该StateStack是怎么进行运作的,后面继续分析。
StateMachine中提供了方法:
protected final void transitionTo(IState destState) {
mSmHandler.transitionTo(destState);
}
同样,还是利用mSmHandler来进行处理,继续分析:
private final void transitionTo(IState destState) {
// mDestState保存当前状态来处理消息;
mDestState = (State) destState;
}
从源码得知,上面提到的状态转换仅仅只是改变了当前的mDestState,其实状态改变并不完整,还需要改变mStateStack。也就是说,当mDestState状态改变的时候,没有同时改变mStateStack,而是等到消息处理派发状态处理完毕的时候做真正的状态调整,即消息处理完毕的时候才更新mStateStack。这样的操作是跟状态的过程相关,使状态的切换和mStateStack的更新独立开来。
状态切换与数据处理图示:
在上面开启状态机的时候,调用sendMessageAtFrontOfQueue()这个方法,而这个方法发送消息用来改变mStateStack。
/** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
跟进这个方法:
public final void handleMessage(Message msg) {
if (!mHasQuit) {
/** Save the current message */
mMsg = msg;
/** State that processed the message */
State msgProcessedState = null;
if (mIsConstructionCompleted) {
...
} else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
&& (mMsg.obj == mSmHandlerObj)) {
/** Initial one time path. */
mIsConstructionCompleted = true;
invokeEnterMethods(0);
} else {
...
}
performTransitions(msgProcessedState, msg);
}
}
从方法的名称我们继续跟进performTransitions(),主要代码如下:
//Do any transitions
private synchronized void performTransitions()
{
while (mDestState != null)
{
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
invokeExitMethods(commonStateInfo);
int stateStackEnteringIndex = moveTempStateStackToStateStack();
invokeEnterMethods(stateStackEnteringIndex);
moveDeferredMessageAtFrontOfQueue();
}
}
这样,状态树的切换主要包含如下几个步骤:
1. 调用transitionTo转换状态,在调用smh.transitionTo()设置目的状态,当消息处理完毕之后调用performTransitions()做真正的状态调整,主要是调整状态栈的节点信息;
2. 根据新的目的节点,调用setupTempStateStackWithStatesToEnter(),查找到根节点中没有被激活的状态,这些节点存储在mTempStateStack中,并获取新目的节点与旧目的节点之间的公共节点commonStateInfo;
3. 根据公共节点commonStateInfo,调用invokeExitMethods(),将需要移除栈的节点的状态设置为false,并且调用exit()方法;
4. 调用moveTempStateStackToStateStack()将mTempStateStack节点反转存储到mStateStack中,这样当前的目的节点置于栈顶,下次处理消息的时候直接调用当前设置的目的节点;
5. 调用invokeEnterMethods()方法,将新增加的节点状态设置为true,并调用enter()方法;
6. 在状态切换之后,如果存在消息没有处理,那么调用moveDeferredMessageAtFrontOfQueue()将延迟消息存放在消息队列的头部。
注意,当前状态的切换在StateMachine中并没有明确,因为这个只是一个状态机负责状态的管理和消息的派发,谁将负责状态的切换还是交给子类决定。
再来一个栗子,还是上面的S0-S7,若初始节点为S4,那么初始状态下,mTempStateStack中存储了S4-S1-S0,mStateStack中存储了:S0-S1-S4,那么mDestState为S4(栈顶);现在状态切换为S7,mDestState为S7;按照父子关系,mTempStateStack中存储的节点为:S7-S2-S0,接下来调用performTransitions()来进行处理:
1. 获取最新目标节点与初始目标节点的公共节点S0;
2. 根据公共节点将初始栈中除公共节点外的其它节点出栈,并且调用出栈节点的exit()方法,并修改节点状态为false;
3. 将mTempStateStack节点反转存储到mStateStack,此时mStateStack中应该存储:S0-S2-S7;
4. 激活新增节点的状态并且调用其enter()。
StateMachine的核心就是SmHandler,它就是一个handler,运行在单独的线程中。Handler原本是用来异步处理派发消息的,在StateMachine中使用Handler来管理各个状态,派发消息处理到各个状态中进行执行。
当状态机Ok的时候(状态加入和状态构建完成)就可以执行某些行为,接收消息并进行处理,派发到当前的状态去执行。StateMachine提供了sendMessage等方法将消息加入到消息队列,当然都是交给SmHandler去做处理,那么核心机制就是Handler的处理消息机制。看一下StateMachine中的handleMessage():
public final void handleMessage(Message msg)
{
//处理当前消息到state中去处理
processMsg(msg);
//消息处理完毕状态切换 更新mStateStack
performTransitions();
}
核心还是processMsg()方法,继续跟进:
/**
* Process the message. If the current state doesn't handle
* it, call the states parent and so on. If it is never handled then
* call the state machines unhandledMessage method.
* @return the state that processed the message
*/
private final State processMsg(Message msg) {
//派发消息到state中进行处理
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
...
while (!curStateInfo.state.processMessage(msg)) {
//当前状态mDestState未处理该消息,交给其parentState处理
curStateInfo = curStateInfo.parentStateInfo;
if (curStateInfo == null) {
/**
* No parents left so it's not handled
*/
mSm.unhandledMessage(msg);
break;
}
}
return (curStateInfo != null) ? curStateInfo.state : null;
}
processMsg首先会判断当前是否退出消息,如果退出消息(isQuite(Msg)),那么就进入mQuittingState状态。
状态机的退出,StateMachine提供了几个方法:
1. quite(): 当队列中的所有消息处理完毕之后,退出状态机;
2. quiteNow(): 不关注队列中的未处理消息,立即退出;
3. transitionToHaltingState(): 直接切换状态到悬置状态,在performTransition()方法中判断如果当前状态为mHaltingState状态,那么通过StateMachine的onHalting()的回调方法通知状态机进行退出。