狀態(tài)機(jī)源碼
com.android.internal.util.StateMachine
狀態(tài)機(jī)初始化
HandlerThread、SmHandler阅签,Loop使用子線程loop。
狀態(tài)機(jī)樹狀結(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)添加過程其實(shí)就是為每個(gè)State創(chuàng)建相應(yīng)的StateInfo對象,通過該對象來建立各個(gè)狀態(tài)之間的關(guān)系脐恩,并且一個(gè)State-StateInfo鍵值對的方式保存到mStateInfo Hash表中抡秆。除了根節(jié)點(diǎn)之外奕巍,每個(gè)節(jié)點(diǎn)狀態(tài)都包含自己的parentState,從形狀上來看儒士,很像二叉樹的結(jié)構(gòu)的止,如圖所示:
啟動狀態(tài)機(jī)
/**
* 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);
}
這個(gè)方法用來設(shè)置狀態(tài)機(jī)的初始化狀態(tài),你可以理解為是個(gè)目標(biāo)狀態(tài)着撩,在執(zhí)行start方法后诅福,狀態(tài)機(jī)會流轉(zhuǎn)到這個(gè)狀態(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é)點(diǎn)的狀態(tài)對象保存在mTempStateStack列表中氓润。
mStateStackTopIndex表示棧頂?shù)膇ndex,這個(gè)值會隨著mStateStack的入棧和出棧發(fā)生變化薯鳍,而moveTempStateStackToStateStack方法是把mTempStateStack中的狀態(tài)反序填充到mStateStack中咖气,這里只是將mStateStack不包含的新狀態(tài)填充進(jìn)來,并不是整體反序填充挖滤,所以要看mStateStack在填充之前的狀態(tài)如何崩溪。
舉個(gè)栗子:
假如目標(biāo)狀態(tài)為S4,則mTempStateStack內(nèi)的狀態(tài)依次為S4,S1,S0壶辜,若mStateStack為空悯舟,則全量反序填充,若mStateStack的棧頂為S1砸民,則只會填充S4抵怎,并且startIndex為2。當(dāng)然岭参,初始化的時(shí)候mStateStack肯定為空反惕,等下分析狀態(tài)切換時(shí)的情況。
@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))演侯,如上段代碼所示姿染,這句話會走到第二個(gè)if條件,其實(shí)就是調(diào)用了各個(gè)狀態(tài)的enter方法,并置mIsConstructionCompleted為false表示初始化完成悬赏,后續(xù)這個(gè)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);
}
}
}
}
這個(gè)方法是從當(dāng)前狀態(tài)向根節(jié)點(diǎn)循環(huán)遍歷盾戴,對當(dāng)前消息進(jìn)行消化,如果返回true,代表當(dāng)前狀態(tài)已處理該消息,否則交給父狀態(tài)處理荞估。
這里有必要提到state的父類,所有的狀態(tài)對象都包含重要的三個(gè)方法衅斩,分別為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;
}
其中前兩個(gè)方法是用來做狀態(tài)流轉(zhuǎn)的怠褐,最后一個(gè)方法是用來做消息處理的畏梆。注意這三個(gè)方法都是在HandlerThread線程中調(diào)用的,不能直接操作ui惫搏。
狀態(tài)切換
狀態(tài)切換都發(fā)生在performTransitions();方法中具温,也就是說在每一次消息處理之后,就會進(jìn)行狀態(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)茴丰,正常情況下是空的达皿,也就不會進(jìn)行狀態(tài)的切換,當(dāng)調(diào)用transitionTo方法贿肩,代表設(shè)置了目標(biāo)狀態(tài)峦椰,這時(shí)候就會進(jìn)入循環(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建立一個(gè)mTempStateStack列表汰规,注意setupTempStateStackWithStatesToEnter方法的whie循環(huán)汤功,在while循環(huán)時(shí)判斷了active的狀態(tài),而且使用的do while循環(huán)溜哮,這個(gè)方法會讓它找到交叉點(diǎn)的位置滔金,并且包含交叉點(diǎn),而如果沒有交叉點(diǎn)茂嗓,就會返回另一棵樹的根節(jié)點(diǎn)餐茵。
舉個(gè)栗子:
假設(shè)當(dāng)前棧為S0,S1,S3,這時(shí)候我要切換到目標(biāo)狀態(tài)S4時(shí)述吸,這個(gè)方法建立的mTempStateStack列表為S4,S1忿族,而返回的正是S1。
這時(shí)候invokeExitMethods方法剛好倒序遍歷當(dāng)前mStateStack并依次退棧,直到命中交叉點(diǎn)為止道批,否則就全部退棧错英。
這個(gè)特性說明,每個(gè)狀態(tài)的enter和exit方法只執(zhí)行一次屹徘,當(dāng)狀態(tài)切換時(shí)走趋,路徑上已經(jīng)存在并激活的狀態(tài)衅金,不會重新走exit和enter方法噪伊。
最后調(diào)用invokeEnterMethods方法,會把從交叉點(diǎn)到目標(biāo)節(jié)點(diǎn)的所有狀態(tài)的enter方法按順序調(diào)用一遍氮唯。
以上就完成了這個(gè)狀態(tài)機(jī)的流轉(zhuǎn)過程鉴吹。