Android -- StateMachine解析
Android中,为了管理Wifi各个过程的状态,提出了状态机,即StateMachine的概念。使用StateMachine,可以将繁杂的过程细化为一个个分支状态,易于管理,并且代码的结构也更清晰,易于阅读。WifiStateMachine.java文件就定义了这样一个结构化状态机,它管理Wifi驱动加载、连接、扫描以及断开等各个状态。本文就以WifiStateMachine为入口来简要分析StateMachine(WiFiController实际上也定义了一个结构化状态机)的实现原理。
这里说的每一个state都是一个State类对象,它必须实现processMessage方法,但enter/exit/getName方法是否实现是可选的,这完全取决于设计者的需要。enter/exit方法相当于面向对象编程中的构造和析构过程,它们经常被用于执行某个state的相关初始化和清理工作。getName()方法一般用来返回某个state的名字。
State类的具体实现如下:
/**
* {@hide}
*
* The class for implementing states in a StateMachine
*/
public class State implements IState {
/**
* Constructor
*/
protected State() {
}
/* (non-Javadoc)
* @see com.android.internal.util.IState#enter()
*/
@Override
public void enter() {
}
/* (non-Javadoc)
* @see com.android.internal.util.IState#exit()
*/
@Override
public void exit() {
}
/* (non-Javadoc)
* @see com.android.internal.util.IState#processMessage(android.os.Message)
*/
@Override
public boolean processMessage(Message msg) {
return false;
}
/**
* Name of State for debugging purposes.
*
* This default implementation returns the class name, returning
* the instance name would better in cases where a State class
* is used for multiple states. But normally there is one class per
* state and the class name is sufficient and easy to get. You may
* want to provide a setName or some other mechanism for setting
* another name if the class name is not appropriate.
*
* @see com.android.internal.util.IState#processMessage(android.os.Message)
*/
@Override
public String getName() {
String name = getClass().getName();
int lastDollar = name.lastIndexOf('$');
return name.substring(lastDollar + 1);
}
这里的结构化状态机实际上是一个树状结构。当我们构建一个状态机时,调用addState()方法向状态机中添加state,该方法会建立各个state之间的结构化特性;调用setInitialState()给当前的状态机结构定义一个初始化状态;状态机构造工作完成后,调用start()方法来开启该状态机。
下面我们通过WifiStateMachine状态机的构建过程,来解析StateMachine的创建过程。
WifiStateMachine的定义如下:
public class WifiStateMachine extends StateMachine implements WifiNative.WifiPnoEventHandler {...}
其构造函数的主要部分:
public WifiStateMachine(Context context, String wlanInterface,WifiTrafficPoller trafficPoller) {
...
addState(mDefaultState);
addState(mInitialState, mDefaultState);
addState(mSupplicantStartingState, mDefaultState);
addState(mSupplicantStartedState, mDefaultState);
addState(mDriverStartingState, mSupplicantStartedState);
addState(mDriverStartedState, mSupplicantStartedState);
addState(mScanModeState, mDriverStartedState);
addState(mConnectModeState, mDriverStartedState);
addState(mL2ConnectedState, mConnectModeState);
addState(mObtainingIpState, mL2ConnectedState);
addState(mVerifyingLinkState, mL2ConnectedState);
addState(mConnectedState, mL2ConnectedState);
addState(mRoamingState, mL2ConnectedState);
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
addState(mWpsRunningState, mConnectModeState);
addState(mWaitForP2pDisableState, mSupplicantStartedState);
addState(mDriverStoppingState, mSupplicantStartedState);
addState(mDriverStoppedState, mSupplicantStartedState);
addState(mSupplicantStoppingState, mDefaultState);
addState(mSoftApStartingState, mDefaultState);
addState(mSoftApStartedState, mDefaultState);
addState(mTetheringState, mSoftApStartedState);
addState(mTetheredState, mSoftApStartedState);
addState(mUntetheringState, mSoftApStartedState);
setInitialState(mInitialState);
setLogRecSize(ActivityManager.isLowRamDeviceStatic() ? 100 : 3000);
setLogOnlyTransitions(false);
if (VDBG) setDbg(true);
//start the state machine
start();
...
}
由代码可知,为构建一个状态机,我们需要三个步骤:
- 创建某些继承自State的类,代表我们要添加的状态
- 调用addState()方法,添加状态机
- 调用setInitialState()设置初始状态
- 调用start()方法,开启状态机
创建一个State子类的代码如下:
class InitialState extends State {
@Override
public void enter() {
WifiNative.stopHal();
mWifiNative.unloadDriver();
if (mWifiP2pChannel == null) {
mWifiP2pChannel = new AsyncChannel();
mWifiP2pChannel.connect(mContext, getHandler(),
mWifiP2pServiceImpl.getP2pStateMachineMessenger());
}
if (mWifiApConfigChannel == null) {
mWifiApConfigChannel = new AsyncChannel();
mWifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
mContext, getHandler());
mWifiApConfigStore.loadApConfiguration();
mWifiApConfigChannel.connectSync(mContext, getHandler(),
mWifiApConfigStore.getMessenger());
}
if (mWifiConfigStore.enableHalBasedPno.get()) {
// make sure developer Settings are in sync with the config option
mHalBasedPnoEnableInDevSettings = true;
}
}
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, getClass().getSimpleName());
switch (message.what) {
case CMD_START_SUPPLICANT:
if (mWifiNative.loadDriver()) {
try {
mNwService.wifiFirmwareReload(mInterfaceName, "STA");
} catch (Exception e) {
loge("Failed to reload STA firmware " + e);
// Continue
}
try {
// A runtime crash can leave the interface up and
// IP addresses configured, and this affects
// connectivity when supplicant starts up.
// Ensure interface is down and we have no IP
// addresses before a supplicant start.
mNwService.setInterfaceDown(mInterfaceName);
mNwService.clearInterfaceAddresses(mInterfaceName);
// Set privacy extensions
mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
// IPv6 is enabled only as long as access point is connected since:
// - IPv6 addresses and routes stick around after disconnection
// - kernel is unaware when connected and fails to start IPv6 negotiation
// - kernel can start autoconfiguration when 802.1x is not complete
mNwService.disableIpv6(mInterfaceName);
} catch (RemoteException re) {
loge("Unable to change interface settings: " + re);
} catch (IllegalStateException ie) {
loge("Unable to change interface settings: " + ie);
}
/* Stop a running supplicant after a runtime restart
* Avoids issues with drivers that do not handle interface down
* on a running supplicant properly.
*/
mWifiMonitor.killSupplicant(mP2pSupported);
if (WifiNative.startHal() == false) {
/* starting HAL is optional */
loge("Failed to start HAL");
}
if (mWifiNative.startSupplicant(mP2pSupported)) {
setWifiState(WIFI_STATE_ENABLING);
if (DBG) log("Supplicant start successful");
mWifiMonitor.startMonitoring();
transitionTo(mSupplicantStartingState);
} else {
loge("Failed to start supplicant!");
}
} else {
loge("Failed to load driver");
}
break;
case CMD_START_AP:
if (mWifiNative.loadDriver() == false) {
loge("Failed to load driver for softap");
} else {
if (enableSoftAp() == true) {
setWifiApState(WIFI_AP_STATE_ENABLING, 0);
transitionTo(mSoftApStartingState);
} else {
setWifiApState(WIFI_AP_STATE_FAILED,
WifiManager.SAP_START_FAILURE_GENERAL);
transitionTo(mInitialState);
}
}
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
}
这是WifiStateMachine中InitialState类的实现。
下面我们看addState()方法。WifiStateMachine继承自StateMachine,调用addState()实际上是调用父类中的同名方法:
/**
* Add a new state to the state machine, parent will be null
* @param state to add
*/
protected final void addState(State state) {
mSmHandler.addState(state, null);
}
层级调用SmHandler类的方法:
/**
* 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;
}
SmHandler是StateMachine内实现的一个Handler子类,它管理着状态机状态的添加、切换及消息发送等重要功能。SmHandler比较重要的成员变量:
/**true if StateMachine has quit */
private boolean mHasQuit = false;
/** The debug flag */
private boolean mDbg = false;
/** The SmHandler object, identifies that message is internal */
private static final Object mSmHandlerObj = new Object();
/** The current message */
private Message mMsg;
/** A list of log records including messages this state machine has processed */
private LogRecords mLogRecords = new LogRecords();
/** true if construction of the state machine has not been completed */
private boolean mIsConstructionCompleted;
/** Stack used to manage the current hierarchy of states */
private StateInfo mStateStack[];
/** Top of mStateStack */
private int mStateStackTopIndex = -1;
/** A temporary stack used to manage the state stack */
private StateInfo mTempStateStack[];
/** The top of the mTempStateStack */
private int mTempStateStackCount;
/** State used when state machine is halted */
private HaltingState mHaltingState = new HaltingState();
/** State used when state machine is quitting */
private QuittingState mQuittingState = new QuittingState();
/** Reference to the StateMachine */
private StateMachine mSm;
/**
* Information about a state.
* Used to maintain the hierarchy.
*/
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;
/**
* Convert StateInfo to string
*/
@Override
public String toString() {
return "state=" + state.getName() + ",active=" + active + ",parent="
+ ((parentStateInfo == null) ? "null" : parentStateInfo.state.getName());
}
}
/** 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;
根据注释我们可知它们代表的含义,这里就不在赘述。
我们分析addState()的实现,WifiStateMachine中状态机数量多、太复杂。为分析构建过程,我们以源码中提供的一个例子作为本文的示例:
mP0
/ \
mP1 mS0
/ \
mS2 mS1
/ \ \
mS3 mS4 mS5 ---> initial state
创建代码为:
addState(mP0);
addState(mP1, mP0);
addState(mS2, mP1);
addState(mS3, mS2);
addState(mS4, mS2);
addState(mS1, mP1);
addState(mS5, mP1);
addState(mS0, mP0);
setInitialState(mS5);
start();
在addState()方法中,首先判断加入的state的父state是否为空;不为空,接着确定父state是否已经添加到层次结构中,如果没有则递归调用addState()方法,添加父state;为空,则说明这个state是整个状态机树结构的顶点,创建它的stateInfo对象,添加到mStateInfo这个HashMap结构中,它保持了状态机的层次结构性。StateInfo的active属性标示该state的enter方法是否被执行。
每个StateInfo都保存了当前的状态和它的父状态信息。我们从mStateInfo截取mP0到mS2的状态信息来分析它怎么保持各个状态之间的联系的:
HashMap mStateInfo
Stae |
StateInfo |
mP0 |
state = mP0, parentStateInfo 没有信息,根节点 |
mP1 |
state = mP1, parentStateinfo = mP0的StateInfo |
mS2 |
state = mS2, parenetStateInfo = mP1的StateInfo |
这里我们可知,在状态机结构中,从某个子节点开始,我们都能依靠这种联系访问到它层次以上的所有父节点。
添加完各个状态后,就是设置初始节点:
protected final void setInitialState(State initialState) {
mSmHandler.setInitialState(initialState);
}
间接调用SmHandler中的同名方法:
/** @see StateMachine#setInitialState(State) */
private final void setInitialState(State initialState) {
if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName());
mInitialState = initialState;
}
把设置的初始状态保存在SmHandler的一个成员变量中。最后就是调用start()方法,开启状态机:
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();
}
/**
* 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");
}
方法首先计算了该层次状态机的最大深度(树状结构);并创建了两个数组,用于保存状态机运行中的状态。接着调用:
/**
* 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();
}
该方法把从初始节点开始的各个状态的StateInfo保存在mTempStateStack数组中;再调用:
/**
* 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;
}
该方法主要把mTempStateStack数组中保存的信息逆序再保存到mStateStack数组中。
在我们这个示例中:
mTempStateStack[ ]数组的内容是:mS5_StateInfo, mS1_StateInfo, mP1_StateInfo, mP0_StateInfo.
mStateStack[ ]数组的内容是:mP0_StateInfo, mP1_StateInfo, mS1_StateInfo, mS5_StateInfo.
回到completeConstruction()方法中,最后调用sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj))向SmHandler发送一个消息,处理如下:
/**
* 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 (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");
}
}
如果是第一次初始化状态机,进入else if分支,调用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 (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
}
按mStateStack数组中保存的状态机顺序,按从上到下的层次结构依次调用各个State的enter()方法,并将每个State的active标志设为true。到这里,一个状态机的构建,包括添加状态、设置初始状态以及开启状态机的过程就结束了。
最后再介绍一下状态机的转换过程。
在WifiStateMachine中,状态机的状态切换是调用transitionTo()方法,间接调用SmHandler内部的同名方法:
/**
* 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.
*/
protected final void transitionTo(IState destState) {
mSmHandler.transitionTo(destState);
}
/** @see StateMachine#transitionTo(IState) */
private final void transitionTo(IState destState) {
mDestState = (State) destState;
if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
}
在处理SM_INIT_CMD消息时,发现还调用了performTransitions(msgProcessedState, msg);状态机各个状态的切换就是在这个函数中处理的。
mP0
/ \
mP1 mS0
/ \
mS2 mS1
/ \ \
mS3 mS4 mS5 ---> initial state
为了方便分析,现在假设我们要将状态切换到mS4:transitionTo(mS4),来看看这个过程会做哪些重要的处理工作。
首先,会把将要装换的状态保存在SmHandler的mDestState成员变量中,这表示我们的目的状态是mS4。看:
/**
* 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);
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;
}
/**
* After processing all transitions check and
* see if the last transition was to quit or halt.
*/
if (destState != null) {
if (destState == mQuittingState) {
/**
* Call onQuitting to let subclasses cleanup.
*/
mSm.onQuitting();
cleanupAfterQuitting();
} else if (destState == mHaltingState) {
/**
* Call onHalting() if we've transitioned to the halting
* state. All subsequent messages will be processed in
* in the halting state which invokes haltedProcessMessage(msg);
*/
mSm.onHalting();
}
}
}
处理的主要过程在while循环中,首先调用setupTempStateStackWithStatesToEnter()方法:
/**
* Setup the mTempStateStack with the states we are going to enter.
*
* This is found by searching up the destState's ancestors for a
* state that is already active i.e. StateInfo.active == true.
* The destStae and all of its inactive parents will be on the
* TempStateStack as the list of states to enter.
*
* @return StateInfo of the common ancestor for the destState and
* current state or null if there is no common parent.
*/
private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
/**
* Search up the parent list of the destination state for an active
* state. Use a do while() loop as the destState must always be entered
* even if it is active. This can happen if we are exiting/entering
* the current state.
*/
mTempStateStackCount = 0;
StateInfo curStateInfo = mStateInfo.get(destState);
do {
mTempStateStack[mTempStateStackCount++] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
} while ((curStateInfo != null) && !curStateInfo.active);
if (mDbg) {
mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
+ mTempStateStackCount + ",curStateInfo: " + curStateInfo);
}
return curStateInfo;
}
该函数主要是重新设置mTempStateStack()数组,在状态机层次结构中,查询目标状态层次结构之上的所有父类状态,将那些没有调用过其enter()方法的父状态保存在数组中,查询依据就是每个状态的StateInfo的active属性,为false就是没被调用过。这个示例中mTempStateStack[ ]数组需要更新的状态:mS4_StateInfo, mS2_StateInfo。
接着调用invokeExitMethods()函数:
/**
* Call the exit method for each state from the top of stack
* up to the common ancestor state.
*/
private final void invokeExitMethods(StateInfo commonStateInfo) {
while ((mStateStackTopIndex >= 0)
&& (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
State curState = mStateStack[mStateStackTopIndex].state;
if (mDbg) mSm.log("invokeExitMethods: " + curState.getName());
curState.exit();
mStateStack[mStateStackTopIndex].active = false;
mStateStackTopIndex -= 1;
}
}
此处会按原先的 mStateStack[ ]数组中保存的各个状态的顺序依次先调用初始节点及其所有父节点的exit()方法,将active设置为false;这个过程直到遍历的节点是初始节点和目的节点的公共父节点方才停止。
接着调用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;
}
将mTempStateStack[ ]数组中保存的状态信息逆序再存入mStateStack[ ]数组中。
接着调用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 (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
}
再按照mStateStack[ ]数组中存储的顺序依次调用各个状态的enter()方法,并将active属性设置为true。在切换的最后,还会把被状态机延迟处理的消息放到消息队列中的最前列,让切换后的新状态优先处理。至此状态切换的过程基本完毕。
状态机处理消息的函数是SmHandler::processMsg()方法:
/**
* 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 (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");
}
}
/**
* 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;
}
从整个分析过程,可以得出以下几点结论:
- 各个状态机的enter()和exit()方法的调用顺序与C++继承体系中的构造函数与析构函数类似。在树状层次结构中,enter()方法是从父状态到子状态依次调用,而exit()方法刚好相反,从子状态到父状态依次调用。
- 在状态切换过程中,只会有某些状态的enter和exit()方法被调用。在本例中,切换到mS4状态,调用enter和exit()方法的节点是到它们最小的公共父节点mP1为止,且该父节点不被调用。
- 各个状态之间,如果某个消息子状态不能处理,其会被依次上抛给各个父状态,直到被处理为止;最后还是没被处理则会抛弃该消息。