1.构造状态机
StateMachine的构造函数都是protected类型,不能实例化,都是由其子类进行初始化操作。StateMachine有三个重载的构造函数
- 一个是直接启动一个消息循环线程来构造一个状态机
- 一个是通过指定消息循环对象来构造状态机
- 一个是通过获取handler对应的消息循环对象来构造状态机
StateMachine.java->StateMachine()
/**
* Constructor creates a StateMachine with its own thread.
*
* @param name of the state machine
*/
protected StateMachine(String name) {
mSmThread = new HandlerThread(name);
mSmThread.start();
Looper looper = mSmThread.getLooper();
initStateMachine(name, looper);
}
/**
* Constructor creates a StateMachine using the looper.
*
* @param name of the state machine
*/
protected StateMachine(String name, Looper looper) {
initStateMachine(name, looper);
}
/**
* Constructor creates a StateMachine using the handler.
*
* @param name of the state machine
*/
protected StateMachine(String name, Handler handler) {
initStateMachine(name, handler.getLooper());
}
这三种构造函数都会通过initStateMachine函数来初始化该状态机,初始化过程比较简单,就是将状态机名称保存到成员变量mName中,同时创建SmHandler对象,SmHandler是一个Handle对象,用于派发消息。
StateMachine.java->initStateMachine()
/**
* Initialize.
*
* @param looper for this state machine
* @param name of the state machine
*/
private void initStateMachine(String name, Looper looper) {
mName = name;
mSmHandler = new SmHandler(looper, this);
}
SmHandler的构造函数主要是添加两个默认的状态mHaltingState & mQuittingState。
StateMachine.java->SmHandler.class->SmHandler()
/**
* Constructor
*
* @param looper for dispatching messages
* @param sm the hierarchical state machine
*/
private SmHandler(Looper looper, StateMachine sm) {
super(looper);
mSm = sm;
addState(mHaltingState, null);
addState(mQuittingState, null);
}
2.建立树形层次结构
在构造完一个状态机前需要向状态机中添加各种状态,StateMachine提供了两个addState函数来添加状态,通过mSmHandler来完成状态添加过程
StateMachine.java->addState()
/**
* Add a new state to the state machine
* @param state the state to add
* @param parent the parent of state
*/
public final void addState(State state, State parent) {
mSmHandler.addState(state, parent);
}
/**
* Add a new state to the state machine, parent will be null
* @param state to add
*/
public final void addState(State state) {
mSmHandler.addState(state, null);
}
状态添加过程其实就是为每个State创建相应的StateInfo对象,通过该对象来建立各个状态之间的关系,并且一个State-StateInfo键值对的方式保存到mStateInfo Hash表中。StateInfo就是包装State组成一个Node,建立State的父子关系;mStateInfo 用来保存State Machine中的所有State,可以按照树形层次结构组织状态机中的所有状态
StateMachine.java->SmHandler.class->mStateInfo
/** The map of all of the states in the state machine */
private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();
StateMachine.java->SmHandler.class->addState()
/**
* 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) {
if (mDbg) {
mSm.log("addStateInternal: E state=" + state.getName() + ",parent="
+ ((parent == null) ? "" : parent.getName()));
}
StateInfo parentStateInfo = null;
if (parent != null) {
parentStateInfo = mStateInfo.get(parent);
if (parentStateInfo == null) {
// Recursively add our parent as it's not been added yet.
parentStateInfo = addState(parent, null);
}
}
StateInfo stateInfo = mStateInfo.get(state);
if (stateInfo == null) {
stateInfo = new StateInfo();
mStateInfo.put(state, stateInfo);
}
// Validate that we aren't adding the same state in two different hierarchies.
if ((stateInfo.parentStateInfo != null)
&& (stateInfo.parentStateInfo != parentStateInfo)) {
throw new RuntimeException("state already added");
}
stateInfo.state = state;
stateInfo.parentStateInfo = parentStateInfo;
stateInfo.active = false;
if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
return stateInfo;
}
接下来介绍构造以下树形状态机的过程:
sm.addState(mP0,null);
sm.addState(mP1,mP0);
sm.addState(mS2,mP1);
sm.addState(mS3,mS2);
sm.addState(mS4,mS2);
sm.addState(mS1,mP1);
sm.addState(mS5,mS1);
sm.addState(mS0,mP0);
setInitialState(S4); //设置初始状态
1.添加根节点状态过程
对于根节点状态的加入sm.addState(S0,null)代码执行如下
StateMachine.java->SmHandler.class->addState()
private final StateInfo addState(mP0, null) {
StateInfo parentStateInfo = null;
StateInfo stateInfo = mStateInfo.get(mP0);
if (stateInfo == null) {
stateInfo = new StateInfo();
mStateInfo.put(state, stateInfo);
}
stateInfo.state = state;
stateInfo.parentStateInfo = parentStateInfo;
stateInfo.active = false;
return stateInfo;
}
2.添加树枝节点状态过程
对于子节点S1状态的加入过程如下sm.addState(mP1,mP0):
StateMachine.java->SmHandler.class->addState()
private final StateInfo addState(mP1, mP0) {
StateInfo parentStateInfo = null;
if (parent != null) {
parentStateInfo = mStateInfo.get(mP0);
if (parentStateInfo == null) {
// Recursively add our parent as it's not been added yet.
parentStateInfo = addState(parent, null);
}
}
StateInfo stateInfo = mStateInfo.get(mP1);
if (stateInfo == null) {
stateInfo = new StateInfo();
mStateInfo.put(state, stateInfo);
}
//设置mP1状态的信息
stateInfo.state = state;
stateInfo.parentStateInfo = parentStateInfo;
stateInfo.active = false;
return stateInfo;
}
对于其他树枝节点的添加过程类似,这里不在介绍,最后保存在mStateInfo表中的所有状态之间就建立了以下树形关系:
3.启动状态机
当向状态机中添加完所有状态时,通过函数start函数来启动状态机
StateMachine.java->start()
/**
* Start the state machine.
*/
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();
}
调用mSmHandler的completeConstruction函数来完成状态机的构造完成处理
StateMachine.java->SmHandler.class->completeConstruction()
/**
* Complete the construction of the state machine.
*/
private final void completeConstruction() {
if (mDbg) mSm.log("completeConstruction: E");
/**
* Determine the maximum depth of the state hierarchy
* so we can allocate the state stacks.
*/
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;
}
}
if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth);
mStateStack = new StateInfo[maxDepth];
mTempStateStack = new StateInfo[maxDepth];
setupInitialStateStack();
/** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
if (mDbg) mSm.log("completeConstruction: X");
}
并调用函数setupInitialStateStack来填充该堆栈
StateMachine.java->SmHandler.class->setupInitialStateStack()
/**
* Initialize StateStack to mInitialState.
*/
private final void setupInitialStateStack() {
if (mDbg) {
mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName());
}
StateInfo curStateInfo = mStateInfo.get(mInitialState);
for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
mTempStateStack[mTempStateStackCount] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
}
// Empty the StateStack
mStateStackTopIndex = -1;
moveTempStateStackToStateStack();
}
调用函数moveTempStateStackToStateStack将节点以反序方式保存到mStateStack中
StateMachine.java->SmHandler.class->moveTempStateStackToStateStack()
/**
* Move the contents of the temporary stack to the state stack
* reversing the order of the items on the temporary stack as
* they are moved.
*
* @return index into mStateStack where entering needs to start
*/
private final int moveTempStateStackToStateStack() {
int startingIndex = mStateStackTopIndex + 1;
int i = mTempStateStackCount - 1;
int j = startingIndex;
while (i >= 0) {
if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
mStateStack[j] = mTempStateStack[i];
j += 1;
i -= 1;
}
mStateStackTopIndex = j - 1;
if (mDbg) {
mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
+ ",startingIndex=" + startingIndex + ",Top="
+ mStateStack[mStateStackTopIndex].state.getName());
}
return startingIndex;
}
初始化完状态栈后,SmHandler将向消息循环中发送一个SM_INIT_CMD消息
StateMachine.java->SmHandler.class->completeConstruction()
/** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
该消息对应的处理如下:
StateMachine.java->SmHandler.class->handleMessage()
/**
* Handle messages sent to the state machine by calling
* the current state's processMessage. It also handles
* the enter/exit calls and placing any deferred messages
* back onto the queue when transitioning to a new state.
*/
@Override
public final void handleMessage(Message msg) {
if (!mHasQuit) {
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPreHandleMessage(msg);
}
if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
/** Save the current message */
mMsg = msg;
/** State that processed the message */
State msgProcessedState = null;
if (mIsConstructionCompleted) {
/** Normal path */
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);
// We need to check if mSm == null here as we could be quitting.
if (mDbg && mSm != null) mSm.log("handleMessage: X");
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPostHandleMessage(msg);
}
}
}
消息处理过程首先调用invokeEnterMethods函数将mStateStack栈中的所有状态设置为激活状态,同时调用每一个状态的enter()函数
StateMachine.java->SmHandler.class->invokeEnterMethods()
/**
* Invoke the enter method starting at the entering index to top of state stack
*/
private final void invokeEnterMethods(int stateStackEnteringIndex) {
for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
if (stateStackEnteringIndex == mStateStackTopIndex) {
// Last enter state for transition
mTransitionInProgress = false;
}
if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
mTransitionInProgress = false; // ensure flag set to false if no methods called
}
最后调用performTransitions函数来切换状态,同时设置mIsConstructionCompleted为true,表示状态机已经启动完成
4.状态切换
SmHandler在处理每个消息时都会调用performTransitions来检查状态切换
StateMachine.java->SmHandler.class->performTransitions()
/**
* Do any transitions
* @param msgProcessedState is the state that processed the message
*/
private void performTransitions(State msgProcessedState, Message msg) {
/**
* If transitionTo has been called, exit and then enter
* the appropriate states. We loop on this to allow
* enter and exit methods to use transitionTo.
*/
State orgState = mStateStack[mStateStackTopIndex].state;
/**
* Record whether message needs to be logged before we transition and
* and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which
* always set msg.obj to the handler.
*/
boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);
if (mLogRecords.logOnlyTransitions()) {
/** Record only if there is a transition */
if (mDestState != null) {
mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
orgState, mDestState);
}
} else if (recordLogMsg) {
/** Record message */
mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,
mDestState);
}
State destState = mDestState;
if (destState != null) {
/**
* Process the transitions including transitions in the enter/exit methods
*/
while (true) {
if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
/**
* Determine the states to exit and enter and return the
* common ancestor state of the enter/exit states. Then
* invoke the exit methods then the enter methods.
*/
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
// flag is cleared in invokeEnterMethods before entering the target state
mTransitionInProgress = true;
invokeExitMethods(commonStateInfo);
int stateStackEnteringIndex = moveTempStateStackToStateStack();
invokeEnterMethods(stateStackEnteringIndex);
/**
* Since we have transitioned to a new state we need to have
* any deferred messages moved to the front of the message queue
* so they will be processed before any other messages in the
* message queue.
*/
moveDeferredMessageAtFrontOfQueue();
if (destState != mDestState) {
// A new mDestState so continue looping
destState = mDestState;
} else {
// No change in mDestState so we're done
break;
}
}
mDestState = null;
}
如何设置目标状态呢?StateMachine提供了transitionTo函数来切换状态
StateMachine.java->transitionTo()
/**
* transition to destination state. Upon returning
* from processMessage the current state's exit will
* be executed and upon the next message arriving
* destState.enter will be invoked.
*
* this function can also be called inside the enter function of the
* previous transition target, but the behavior is undefined when it is
* called mid-way through a previous transition (for example, calling this
* in the enter() routine of a intermediate node when the current transition
* target is one of the nodes descendants).
*
* @param destState will be the state that receives the next message.
*/
public final void transitionTo(IState destState) {
mSmHandler.transitionTo(destState);
}
该函数直接调用SmHandler的transitionTo函数来实现,SmHandler的transitionTo函数定义如下:
StateMachine.java->SmHandler.class->transitionTo()
/** @see StateMachine#transitionTo(IState) */
private final void transitionTo(IState destState) {
if (mTransitionInProgress) {
Log.wtf(mSm.mName, "transitionTo called while transition already in progress to " +
mDestState + ", new target state=" + destState);
}
//标定目标状态
mDestState = (State) destState;
if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
}
这里只是简单地设置了mDestState变量,并未真正更新状态栈mStateStack,在前面介绍了SmHandler在每次处理消息时都会自动更新一次mStateStack,无论mDestState变量值是否改变。由此可知目标状态的设置与状态栈的更新是异步的。
5.消息处理
StateMachine处理的核心就是SmHandler,就是一个Handler,运行在单独线程中。Handler是用来异步处理派发消息,这里使用Handler管理各个状态,派发消息处理到各个状态中去执行。StateMachine提供了多个消息发送接口,通过这些接口函数可以将消息发送到SmHandler中。
StateMachine.java->sendMessage()
/**
* Enqueue a message to this state machine.
*
* Message is ignored if state machine has quit.
*/
public void sendMessage(int what) {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
smh.sendMessage(obtainMessage(what));
}
SmHandler将处理通过SmHandler发送的消息,处理过程如下:
StateMachine.java->SmHandler.class->handleMessage()
/**
* Handle messages sent to the state machine by calling
* the current state's processMessage. It also handles
* the enter/exit calls and placing any deferred messages
* back onto the queue when transitioning to a new state.
*/
@Override
public final void handleMessage(Message msg) {
if (!mHasQuit) {
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPreHandleMessage(msg);
}
if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
/** Save the current message */
mMsg = msg;
/** State that processed the message */
State msgProcessedState = null;
if (mIsConstructionCompleted) {
/** Normal path */
//因为状态机已经构建完毕,所以消息在此处理,如果处理成功,则返回处理此消息的state
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);
}
//消息处理完毕,更新mStateStack
performTransitions(msgProcessedState, msg);
// We need to check if mSm == null here as we could be quitting.
if (mDbg && mSm != null) mSm.log("handleMessage: X");
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPostHandleMessage(msg);
}
}
}
变量mIsConstructionCompleted在状态机启动完成后被设置为true,因此这里将调用processMsg函数来完成消息处理。
StateMachine.java->SmHandler.class->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) {
//从栈顶开始处理
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
if (mDbg) {
mSm.log("processMsg: " + curStateInfo.state.getName());
}
if (isQuit(msg)) {
transitionTo(mQuittingState);
} else {
//当前状态处理
while (!curStateInfo.state.processMessage(msg)) {
/**
* Not processed
*/
//如果未能处理,则交由父状态处理
curStateInfo = curStateInfo.parentStateInfo;
if (curStateInfo == null) {
/**
* No parents left so it's not handled
*/
//如果一直没有被处理,则标记为未处理消息
mSm.unhandledMessage(msg);
break;
}
if (mDbg) {
mSm.log("processMsg: " + curStateInfo.state.getName());
}
}
}
return (curStateInfo != null) ? curStateInfo.state : null;
}
消息处理过程是从mStateStack栈顶派发到栈底,直到该消息被处理!