状态机源码
com.android.internal.util.StateMachine
状态机初始化
HandlerThread、SmHandler,Loop使用子线程loop。
状态机树状结构
private final StateInfo addState(State state, State parent) {
if (mDbg) {
Log.d(TAG, "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) Log.d(TAG, "addStateInternal: X stateInfo: " + stateInfo);
return stateInfo;
}
状态添加过程其实就是为每个State创建相应的StateInfo对象,通过该对象来建立各个状态之间的关系,并且一个State-StateInfo键值对的方式保存到mStateInfo Hash表中。除了根节点之外,每个节点状态都包含自己的parentState,从形状上来看,很像二叉树的结构,如图所示:
启动状态机
/**
* Set the initial state. This must be invoked before
* and messages are sent to the state machine.
*
* @param initialState is the state which will receive the first message.
*/
protected final void setInitialState(State initialState) {
mSmHandler.setInitialState(initialState);
}
这个方法用来设置状态机的初始化状态,你可以理解为是个目标状态,在执行start方法后,状态机会流转到这个状态下。
public void start() {
// mSmHandler can be null if the state machine has quit.
if (mSmHandler == null) return;
/** Send the complete construction message */
mSmHandler.completeConstruction();
}
private final void completeConstruction() {
if (mDbg) Log.d(TAG, "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) Log.d(TAG, "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) Log.d(TAG, "completeConstruction: X");
}
private final void setupInitialStateStack() {
//在mStateInfo中取得初始状态mInitialState对应的StateInfo
StateInfo curStateInfo = mStateInfo.get(mInitialState);
//从初始状态mInitialState开始根据父子关系填充mTempStateStack堆栈
for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
mTempStateStack[mTempStateStackCount] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
}
// Empty the StateStack
mStateStackTopIndex = -1;
//将mTempStateStack中的状态按反序方式移动到mStateStack栈中
moveTempStateStackToStateStack();
}
private final int moveTempStateStackToStateStack() {
//startingIndex= 0
int startingIndex = mStateStackTopIndex + 1;
int i = mTempStateStackCount - 1;
int j = startingIndex;
while (i >= 0) {
if (mDbg) Log.d(TAG, "moveTempStackToStateStack: i=" + i + ",j=" + j);
mStateStack[j] = mTempStateStack[i];
j += 1;
i -= 1;
}
mStateStackTopIndex = j - 1;
return startingIndex;
}
setupInitialStateStack方法是根据初始化状态以及树状结构关系填充mTempStateStack,可以理解为把当前状态到根节点的状态对象保存在mTempStateStack列表中。
mStateStackTopIndex表示栈顶的index,这个值会随着mStateStack的入栈和出栈发生变化,而moveTempStateStackToStateStack方法是把mTempStateStack中的状态反序填充到mStateStack中,这里只是将mStateStack不包含的新状态填充进来,并不是整体反序填充,所以要看mStateStack在填充之前的状态如何。
举个栗子:
假如目标状态为S4,则mTempStateStack内的状态依次为S4,S1,S0,若mStateStack为空,则全量反序填充,若mStateStack的栈顶为S1,则只会填充S4,并且startIndex为2。当然,初始化的时候mStateStack肯定为空,等下分析状态切换时的情况。
@Override
public final void handleMessage(Message msg) {
if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what);
/** Save the current message */
mMsg = msg;
if (mIsConstructionCompleted) {
/** Normal path */
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();
if (mDbg) Log.d(TAG, "handleMessage: X");
}
当栈状态初始化完成后执行了sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj)),如上段代码所示,这句话会走到第二个if条件,其实就是调用了各个状态的enter方法,并置mIsConstructionCompleted为false表示初始化完成,后续这个if条件不会再调用。
消息处理
sendMessage
StateMachine的sendMessage等一系列方法,都是用来做消息处理的,调用到processMsg方法
private final void processMsg(Message msg) {
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
if (mDbg) {
Log.d(TAG, "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) {
Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
}
}
/**
* Record that we processed the message
*/
if (mSm.recordLogRec(msg)) {
if (curStateInfo != null) {
State orgState = mStateStack[mStateStackTopIndex].state;
mLogRecords.add(msg, mSm.getLogRecString(msg), curStateInfo.state,
orgState);
} else {
mLogRecords.add(msg, mSm.getLogRecString(msg), null, null);
}
}
}
}
这个方法是从当前状态向根节点循环遍历,对当前消息进行消化,如果返回true,代表当前状态已处理该消息,否则交给父状态处理。
这里有必要提到state的父类,所有的状态对象都包含重要的三个方法,分别为enter、exit和processMessage
@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;
}
其中前两个方法是用来做状态流转的,最后一个方法是用来做消息处理的。注意这三个方法都是在HandlerThread线程中调用的,不能直接操作ui。
状态切换
状态切换都发生在performTransitions();方法中,也就是说在每一次消息处理之后,就会进行状态的切换,当然有可能切换也有可能不切换,具体看mDestState变量。
private void performTransitions() {
/**
* 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 destState = null;
while (mDestState != null) {
if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");
/**
* Save mDestState locally and set to null
* to know if enter/exit use transitionTo.
*/
destState = mDestState;
mDestState = null;
/**
* 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();
}
/**
* 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();
}
}
}
protected final void transitionTo(IState destState) {
mSmHandler.transitionTo(destState);
}
private final void transitionTo(IState destState) {
mDestState = (State) destState;
if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName())
}
mDestState对象代表目标状态,正常情况下是空的,也就不会进行状态的切换,当调用transitionTo方法,代表设置了目标状态,这时候就会进入循环。这段代码的核心在这几行:
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
invokeExitMethods(commonStateInfo);
int stateStackEnteringIndex = moveTempStateStackToStateStack();
invokeEnterMethods(stateStackEnteringIndex);
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) {
Log.d(TAG, "setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
+ mTempStateStackCount + ",curStateInfo: " + curStateInfo);
}
return curStateInfo;
}
private final void invokeExitMethods(StateInfo commonStateInfo) {
while ((mStateStackTopIndex >= 0) &&
(mStateStack[mStateStackTopIndex] != commonStateInfo)) {
State curState = mStateStack[mStateStackTopIndex].state;
if (mDbg) Log.d(TAG, "invokeExitMethods: " + curState.getName());
curState.exit();
mStateStack[mStateStackTopIndex].active = false;
mStateStackTopIndex -= 1;
}
}
/**
* 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) Log.d(TAG, "invokeEnterMethods: " + mStateStack[i].state.getName());
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
}
setupTempStateStackWithStatesToEnter方法是基于目标状态destState建立一个mTempStateStack列表,注意setupTempStateStackWithStatesToEnter方法的whie循环,在while循环时判断了active的状态,而且使用的do while循环,这个方法会让它找到交叉点的位置,并且包含交叉点,而如果没有交叉点,就会返回另一棵树的根节点。
举个栗子:
假设当前栈为S0,S1,S3,这时候我要切换到目标状态S4时,这个方法建立的mTempStateStack列表为S4,S1,而返回的正是S1。
这时候invokeExitMethods方法刚好倒序遍历当前mStateStack并依次退栈,直到命中交叉点为止,否则就全部退栈。
这个特性说明,每个状态的enter和exit方法只执行一次,当状态切换时,路径上已经存在并激活的状态,不会重新走exit和enter方法。
最后调用invokeEnterMethods方法,会把从交叉点到目标节点的所有状态的enter方法按顺序调用一遍。
以上就完成了这个状态机的流转过程。