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

狀態(tài)機(jī)源碼

com.android.internal.util.StateMachine

狀態(tài)機(jī)初始化

image.png

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

image.png

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市惩琉,隨后出現(xiàn)的幾起案子豆励,更是在濱河造成了極大的恐慌,老刑警劉巖瞒渠,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件良蒸,死亡現(xiàn)場離奇詭異,居然都是意外死亡伍玖,警方通過查閱死者的電腦和手機(jī)嫩痰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來窍箍,“玉大人串纺,你說我怎么就攤上這事∫” “怎么了纺棺?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長邪狞。 經(jīng)常有香客問我祷蝌,道長,這世上最難降的妖魔是什么帆卓? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任巨朦,我火速辦了婚禮,結(jié)果婚禮上鳞疲,老公的妹妹穿的比我還像新娘罪郊。我一直安慰自己,他們只是感情好尚洽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布悔橄。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪癣疟。 梳的紋絲不亂的頭發(fā)上挣柬,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天,我揣著相機(jī)與錄音睛挚,去河邊找鬼邪蛔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛扎狱,可吹牛的內(nèi)容都是我干的侧到。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼淤击,長吁一口氣:“原來是場噩夢啊……” “哼匠抗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起污抬,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤汞贸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后印机,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矢腻,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年射赛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了多柑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咒劲,死狀恐怖顷蟆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腐魂,我是刑警寧澤帐偎,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站蛔屹,受9級特大地震影響削樊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜兔毒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一漫贞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧育叁,春花似錦迅脐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豌骏。三九已至,卻和暖如春隐锭,著一層夾襖步出監(jiān)牢的瞬間窃躲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工钦睡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蒂窒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓荞怒,卻偏偏與公主長得像洒琢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子挣输,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評論 2 354

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

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