狀態(tài)機源碼分析

狀態(tài)機源碼

com.android.internal.util.StateMachine

狀態(tài)機初始化

image.png

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),如圖所示:

image.png

啟動狀態(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)過程。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • StateMachine 的簡單使用 步驟 源碼的frameworks/base/core/java/com/an...
    JustinBetter閱讀 7,363評論 3 7
  • java筆記第一天 == 和 equals ==比較的比較的是兩個變量的值是否相等,對于引用型變量表示的是兩個變量...
    jmychou閱讀 1,524評論 0 3
  • 概念 有限狀態(tài)機即FSM,簡稱狀態(tài)機,表示有限個狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動作等行為的數(shù)學(xué)模型。 狀態(tài)機可以描...
    Galileo_404閱讀 10,788評論 0 8
  • 有人的地方就有故事,無論那個地方是大城市還是小山村,生活在那里的人們每天都會上演屬于自己的人生。 也許生活在自己兩...
    孫小山閱讀 299評論 4 0
  • 南方的冬天,放眼望去,竟也是滿眼綠意,要不是身上穿著厚厚的棉襖,倒也讓人恍惚這究竟是冬天,還是春天。
    神喵君閱讀 185評論 0 0