狀態(tài)機源碼
com.android.internal.util.StateMachine
狀態(tài)機初始化
HandlerThread、SmHandler,Loop使用子線程loop。
狀態(tài)機樹狀結(jié)構(gòu)
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;
}
狀態(tài)添加過程其實就是為每個State創(chuàng)建相應(yīng)的StateInfo對象,通過該對象來建立各個狀態(tài)之間的關(guān)系,并且一個State-StateInfo鍵值對的方式保存到mStateInfo Hash表中。除了根節(jié)點之外,每個節(jié)點狀態(tài)都包含自己的parentState,從形狀上來看,很像二叉樹的結(jié)構(gòu),如圖所示:
啟動狀態(tài)機
/**
* 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);
}
這個方法用來設(shè)置狀態(tài)機的初始化狀態(tài),你可以理解為是個目標(biāo)狀態(tài),在執(zhí)行start方法后,狀態(tài)機會流轉(zhuǎn)到這個狀態(tài)下。
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];
//設(shè)置狀態(tài)堆棧
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中取得初始狀態(tài)mInitialState對應(yīng)的StateInfo
StateInfo curStateInfo = mStateInfo.get(mInitialState);
//從初始狀態(tài)mInitialState開始根據(jù)父子關(guān)系填充mTempStateStack堆棧
for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
mTempStateStack[mTempStateStackCount] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
}
// Empty the StateStack
mStateStackTopIndex = -1;
//將mTempStateStack中的狀態(tài)按反序方式移動到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方法是根據(jù)初始化狀態(tài)以及樹狀結(jié)構(gòu)關(guān)系填充mTempStateStack,可以理解為把當(dāng)前狀態(tài)到根節(jié)點的狀態(tài)對象保存在mTempStateStack列表中。
mStateStackTopIndex表示棧頂?shù)膇ndex,這個值會隨著mStateStack的入棧和出棧發(fā)生變化,而moveTempStateStackToStateStack方法是把mTempStateStack中的狀態(tài)反序填充到mStateStack中,這里只是將mStateStack不包含的新狀態(tài)填充進來,并不是整體反序填充,所以要看mStateStack在填充之前的狀態(tài)如何。
舉個栗子:
假如目標(biāo)狀態(tài)為S4,則mTempStateStack內(nèi)的狀態(tài)依次為S4,S1,S0,若mStateStack為空,則全量反序填充,若mStateStack的棧頂為S1,則只會填充S4,并且startIndex為2。當(dāng)然,初始化的時候mStateStack肯定為空,等下分析狀態(tài)切換時的情況。
@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");
}
當(dāng)棧狀態(tài)初始化完成后執(zhí)行了sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj)),如上段代碼所示,這句話會走到第二個if條件,其實就是調(diào)用了各個狀態(tài)的enter方法,并置mIsConstructionCompleted為false表示初始化完成,后續(xù)這個if條件不會再調(diào)用。
消息處理
sendMessage
StateMachine的sendMessage等一系列方法,都是用來做消息處理的,調(diào)用到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);
}
}
}
}
這個方法是從當(dāng)前狀態(tài)向根節(jié)點循環(huán)遍歷,對當(dāng)前消息進行消化,如果返回true,代表當(dāng)前狀態(tài)已處理該消息,否則交給父狀態(tài)處理。
這里有必要提到state的父類,所有的狀態(tài)對象都包含重要的三個方法,分別為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;
}
其中前兩個方法是用來做狀態(tài)流轉(zhuǎn)的,最后一個方法是用來做消息處理的。注意這三個方法都是在HandlerThread線程中調(diào)用的,不能直接操作ui。
狀態(tài)切換
狀態(tài)切換都發(fā)生在performTransitions();方法中,也就是說在每一次消息處理之后,就會進行狀態(tài)的切換,當(dāng)然有可能切換也有可能不切換,具體看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對象代表目標(biāo)狀態(tài),正常情況下是空的,也就不會進行狀態(tài)的切換,當(dāng)調(diào)用transitionTo方法,代表設(shè)置了目標(biāo)狀態(tài),這時候就會進入循環(huán)。這段代碼的核心在這幾行:
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方法是基于目標(biāo)狀態(tài)destState建立一個mTempStateStack列表,注意setupTempStateStackWithStatesToEnter方法的whie循環(huán),在while循環(huán)時判斷了active的狀態(tài),而且使用的do while循環(huán),這個方法會讓它找到交叉點的位置,并且包含交叉點,而如果沒有交叉點,就會返回另一棵樹的根節(jié)點。
舉個栗子:
假設(shè)當(dāng)前棧為S0,S1,S3,這時候我要切換到目標(biāo)狀態(tài)S4時,這個方法建立的mTempStateStack列表為S4,S1,而返回的正是S1。
這時候invokeExitMethods方法剛好倒序遍歷當(dāng)前mStateStack并依次退棧,直到命中交叉點為止,否則就全部退棧。
這個特性說明,每個狀態(tài)的enter和exit方法只執(zhí)行一次,當(dāng)狀態(tài)切換時,路徑上已經(jīng)存在并激活的狀態(tài),不會重新走exit和enter方法。
最后調(diào)用invokeEnterMethods方法,會把從交叉點到目標(biāo)節(jié)點的所有狀態(tài)的enter方法按順序調(diào)用一遍。
以上就完成了這個狀態(tài)機的流轉(zhuǎn)過程。