[Android] 狀態(tài)機(jī) StateMachine 源碼剖析

1. 案例

案例:我們常見的汽車亿胸,我們可以使用它行駛坯钦,也可以將它停止在路邊预皇。當(dāng)它在行駛的過程中,需要不斷的檢測(cè)油量婉刀,一旦油量不足的時(shí)候吟温,就將陷入停止?fàn)顟B(tài)。而停止在路邊的汽車突颊,需要點(diǎn)火啟動(dòng)鲁豪,此時(shí)將檢測(cè)車中的油量,當(dāng)油量不足的時(shí)候律秃,汽車就需要去加油站加油爬橡。

當(dāng)我們對(duì)汽車的狀態(tài)和行為進(jìn)行抽象,汽車的狀態(tài)可以有 :

  • 停車 STOP
  • 行駛 RUN
  • 檢測(cè)油量 CHECK_OIL
  • 加油 ADDING_OIL

而我們可以對(duì)汽車的操作可以是:

  • 停車 ACTION_STOP
  • 行駛 ACTION_RUN
  • 加油 ACTION_ADD_OIL

我們建立一個(gè)二維表棒动,將狀態(tài)和可操作的行為組合在一起:

狀態(tài)行為對(duì)應(yīng)表

2. HSM

我們通過這個(gè)狀態(tài)表構(gòu)建我們的狀態(tài)引用關(guān)系模型:

狀態(tài)轉(zhuǎn)化模型

這幅狀態(tài)圖實(shí)際上一個(gè)相對(duì)復(fù)雜的網(wǎng)狀圖形糙申,當(dāng)構(gòu)建一個(gè)更為復(fù)雜的系統(tǒng)的時(shí)候,這種網(wǎng)狀圖將會(huì)以成倍的復(fù)雜性遞增船惨。為了解決這個(gè)問題郭宝,我們需要將這種網(wǎng)狀的狀態(tài)機(jī)轉(zhuǎn)化為一個(gè)樹狀的層次狀態(tài)機(jī),也叫 HSM (Hierarchical State Machine)掷漱。我們可以將上述的狀態(tài)模型轉(zhuǎn)化為:

層次狀態(tài)圖

這張圖里粘室,將 STOP 作為根節(jié)點(diǎn),從層次上作為其他狀態(tài)節(jié)點(diǎn)的父節(jié)點(diǎn)卜范。

  • STOP 作為初始狀態(tài)
  • 發(fā)生了 ACTION_ADD_OIL 動(dòng)作衔统,STOP 狀態(tài)就變成了 ADDING_OIL 狀態(tài)
  • 當(dāng)ADDING_OIL 結(jié)束,發(fā)生了 ACTION_RUN 動(dòng)作海雪,就需要彈出 ADDING_OIL狀態(tài) 锦爵,傳入到 CHECK_OIL,然后傳入 RUN 狀態(tài)奥裸。

3. [StateMachine] 初始化

StateMachineAndroid 系統(tǒng)提供的 HSM 狀態(tài)機(jī)的實(shí)現(xiàn),它的源碼在包com.android.internal.util下险掀。StateMachine 提供了三個(gè)構(gòu)造方法,但這三個(gè)方法大同小異:

protected StateMachine(String name) {
        mSmThread = new HandlerThread(name);
        mSmThread.start();
        Looper looper = mSmThread.getLooper();

        initStateMachine(name, looper);
}

構(gòu)造器調(diào)用 initStateMachine 函數(shù),這個(gè)函數(shù)需要傳入了一個(gè) Looper 對(duì)象湾宙,StateMachine 對(duì)象所有的操作都需要在這個(gè) Looper 所在的線程中運(yùn)行樟氢。而之間的通訊是通過 SmHandler 對(duì)象傳遞。

private void initStateMachine(String name, Looper looper) {
        mName = name;
        mSmHandler = new SmHandler(looper, this);
    }

上面我們說了侠鳄,StateMachineHSM 狀態(tài)機(jī)埠啃,構(gòu)造它的時(shí)候,需要指定它的層次關(guān)系伟恶,這需要調(diào)用 addState 函數(shù)碴开,這個(gè)函數(shù)有兩個(gè)參數(shù),第第二個(gè)參數(shù)代表的是第一個(gè)參數(shù)的父節(jié)點(diǎn):

protected final void addState(State state, State parent) {
        mSmHandler.addState(state, parent);
}

而根節(jié)點(diǎn),又稱為初始狀態(tài)節(jié)點(diǎn)潦牛,需要通過 setInitialState 函數(shù)指定:

 protected final void setInitialState(State initialState) {
        mSmHandler.setInitialState(initialState);
}

這里眶掌,不論設(shè)置什么樣的節(jié)點(diǎn),都需要通過 mSmHandler 對(duì)象設(shè)置,比如巴碗,當(dāng)通過調(diào)用 StateMachine.addState 添加節(jié)點(diǎn)的時(shí)候朴爬,需要調(diào)用到 SmHandler.addState 函數(shù):

//code SmHandler
private final StateInfo addState(State state, State parent) {
            if (mDbg) {
            //debug開關(guān)可以通過 StateMachine.setDbg接口設(shè)置打開
                mSm.log("addStateInternal: E state=" + state.getName() + ",parent="
                        + ((parent == null) ? "" : parent.getName()));
            }
            StateInfo parentStateInfo = null;
            // StateInfo 表示在 HSM 樹中的狀態(tài)節(jié)點(diǎn)
            if (parent != null) {
                parentStateInfo = mStateInfo.get(parent);
                //mStateInfo 是一個(gè)hashmap對(duì)象
                if (parentStateInfo == null) {
                //當(dāng)父節(jié)點(diǎn)不存在的時(shí)候,添加該節(jié)點(diǎn)
                    // Recursively add our parent as it's not been added yet.
                    parentStateInfo = addState(parent, null);
                }
            }
            StateInfo stateInfo = mStateInfo.get(state);
            //通過狀態(tài)構(gòu)建一個(gè)狀態(tài)節(jié)點(diǎn)
            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)) {
                    //不允許一個(gè)節(jié)點(diǎn)存在兩個(gè)父節(jié)點(diǎn)
                throw new RuntimeException("state already added");
            }
            stateInfo.state = state;
            stateInfo.parentStateInfo = parentStateInfo;
            //構(gòu)建父子的層次關(guān)系
            stateInfo.active = false;
            if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
            return stateInfo;
        }

mStateInfo是一個(gè) HashMap<State, StateInfo> 類型的對(duì)象良价, 而 StateInfo 類是用于記錄狀態(tài) State 對(duì)象信息,和父節(jié)點(diǎn)信息的 HSM 節(jié)點(diǎn)對(duì)象

 private class StateInfo {
            /** The state */
            State state;

            /** The parent of this state, null if there is no parent */
            StateInfo parentStateInfo;

            /** True when the state has been entered and on the stack */
            boolean active;
    
 }

按照我們剛才對(duì) Car 這個(gè)模型的抽象蒿叠,我們可以定義出一個(gè) Car 的狀態(tài)機(jī):


public class Car extends StateMachine {
    ....
    public Car(String name) {
        super(name);
        this.addState(mStopState,null);
        //mStopState 作為根節(jié)點(diǎn)狀態(tài)明垢,沒有父節(jié)點(diǎn)
            this.addState(mAddOilState,mStopState);
            //mAddOilState 作為mStopState 的子狀態(tài)
            this.addState(mCheckOilState,mStopState);
            //mCheckOilState 作為mStopState 的子狀態(tài)
                this.addState(mRunState,mCheckOilState);
                //mRunState 作為mCheckOilState 的子狀態(tài)
        this.setInitialState(mStopState);
        // mStopState 為初始狀態(tài)
    }
}

當(dāng)我們構(gòu)造完我們的樹形結(jié)構(gòu)了以后,我們就可以將我們的狀態(tài)機(jī)啟動(dòng)起來市咽,這個(gè)啟動(dòng)依賴于 StateMachine.start 函數(shù):

public void start() {
        // mSmHandler can be null if the state machine has quit.
        SmHandler smh = mSmHandler;
        if (smh == null) return;
        /** Send the complete construction message */
        smh.completeConstruction();//調(diào)用SmHandler.completeConstruction
}

StateMachine.start 中調(diào)用 SmHandler.completeConstruction用于提交我們之前的所有操作:

 private final void completeConstruction() {
            if (mDbg) mSm.log("completeConstruction: E");

            /**
             * Determine the maximum depth of the state hierarchy
             * so we can allocate the state stacks.
             */
            int maxDepth = 0;// step1
            for (StateInfo si : mStateInfo.values()) {
                int depth = 0;
                for (StateInfo i = si; i != null; depth++) {
                    i = i.parentStateInfo;
                }
                if (maxDepth < depth) {
                    maxDepth = depth;//找到一個(gè)最深的堆棧
                }
            }
            if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth);

            mStateStack = new StateInfo[maxDepth];
            mTempStateStack = new StateInfo[maxDepth];//用于計(jì)算的臨時(shí)變量
            setupInitialStateStack();//以初始狀態(tài)為棧底保存到 mStateStack

            /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
            sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
            if (mDbg) mSm.log("completeConstruction: X");
        }

按照我們的 HSM 模型痊银,以 STOP 狀態(tài)為基礎(chǔ)狀態(tài)的時(shí)候,那么我們以這個(gè)狀態(tài)為棧底向上延伸施绎,我們可以得到兩個(gè)棧溯革,分別是:

stack1: [STOP,CHECK_OIL,RUN]

stack2: [STOP,ADD_OIL]

stack1 的最大深度為 3 , stack2 的最大深度為 2 。那么 stack1 就可以應(yīng)用于 stack2 的情況谷醉。 completeConstruction 代碼中 step1 段的代碼就是這個(gè)目的致稀,找到一個(gè)最大的棧,用于給所有的棧情況使用俱尼。

private final void setupInitialStateStack() {
            if (mDbg) {
                mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName());
            }

            StateInfo curStateInfo = mStateInfo.get(mInitialState);
            for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
                mTempStateStack[mTempStateStackCount] = curStateInfo;
                curStateInfo = curStateInfo.parentStateInfo;
            }
            //將初始狀態(tài)位根狀態(tài)以0->N的順序存入 tempStack

            // Empty the StateStack
            mStateStackTopIndex = -1;
            moveTempStateStackToStateStack();//將 tempstack 倒敘復(fù)制給 stateStack
        }

mTempStateStack 是一個(gè)中間變量抖单,它存的是倒敘的 mStateStack 。比如我們的初始狀態(tài)是 RUN 遇八。那么我們需要不斷循環(huán)將 RUN 的父節(jié)點(diǎn)存入 mTempStateStack 得到:

mTempStateStack :[RUN,CHECK_OIL,STOP]

這時(shí)候我們需要調(diào)用 moveTempStateStackToStateStack 函數(shù)將它倒敘復(fù)制到 mStateStack 對(duì)象中矛绘,保證當(dāng)前狀態(tài) RUN 位于棧頂:

mStateStack: [STOP,CHECK_OIL,RUN]

mStateStackTopIndex 變量指向 mStateStack 的棧頂。剛才的這個(gè)例子刃永,mStateStackTopIndex 的值為 2 ,指向 RUN 所在的數(shù)組索引位置货矮。

到了 start 函數(shù)調(diào)用的這一步,我們就完成了一個(gè)樹形數(shù)據(jù)結(jié)構(gòu)和初始狀態(tài)的設(shè)置斯够,接下來囚玫,我們就可以往我們的狀態(tài)機(jī)上發(fā)送我們的指令。

4. [StateMachine] 處理消息

我們通過上面的手段構(gòu)造完一個(gè)狀態(tài)機(jī)以后读规,就可以通過指令讓這個(gè)狀態(tài)機(jī)去處理消息了劫灶。我們先給我們的狀態(tài)機(jī)開一些外部調(diào)用的接口:

public interface ICar {
    
    public void run();
    public void stop();
    public void addOil();
    
}

public class Car extends StateMachine implements ICar{
    ....
}

public void func() {
    ICar car = new Car("Ford");
    car.addOil();
    car.run();
    car.stop();
}

當(dāng)我們要向我們的狀態(tài)機(jī)發(fā)送指令的時(shí)候,需要調(diào)用狀態(tài)機(jī)的 sendMessage(...) 函數(shù)掖桦,這套函數(shù)跟 android.os.Handler 提供的 api 的含義一模一樣本昏。實(shí)際上,狀態(tài)機(jī)在處理這種消息的時(shí)候枪汪,也是采用 Handler 的方式涌穆,而我們上面反復(fù)提到的 SmHandler 對(duì)象實(shí)際上就是 Handler 對(duì)象的子類怔昨。

public final void sendMessage(int what) {
        // mSmHandler can be null if the state machine has quit.
        SmHandler smh = mSmHandler;
        if (smh == null) return;

        smh.sendMessage(obtainMessage(what));//通過Handler方式發(fā)送消息
}

這樣,我們就可以通過這個(gè)函數(shù)去實(shí)現(xiàn)我們的幾個(gè)接口方法:

public class Car extends StateMachine implements ICar{ 
    ...
    public void run() {
        this.sendMessage(ACTION_RUN);
    } 
    
    public void stop() {
        this.sendMessage(ACTION_STOP);
    }
    
    public void addOil() {
        this.sendMessage(ACTION_ADD_OIL);
    }
}

根據(jù)我們對(duì) Handler 類的了解宿稀,每當(dāng)我們通過 Handler.sendMessage 函數(shù)發(fā)送一個(gè)消息的時(shí)候趁舀,都將在 Looper 的下個(gè)處理消息執(zhí)行的時(shí)候,回調(diào) Handler.handleMessage(Message msg) 方法祝沸。由于 SmHandler 繼承于 Handler,并且它復(fù)寫了 handleMessage 函數(shù)矮烹,因此 , 消息發(fā)送之后罩锐,最后將回調(diào)到SmHandler.handleMessage 方法中奉狈。

//code SmHandler
public final void handleMessage(Message msg) {
            if (!mHasQuit) {
                if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);

                /** Save the current message */
                mMsg = msg;

                /** State that processed the message */
                State msgProcessedState = null;
                if (mIsConstructionCompleted) {
                    /** Normal path */
                    msgProcessedState = processMsg(msg);
                    //由當(dāng)前狀態(tài)處理
                } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
                        && (mMsg.obj == mSmHandlerObj)) {
                    /** Initial one time path. */
                    //執(zhí)行初始化操作函數(shù)
                    mIsConstructionCompleted = true;
                    invokeEnterMethods(0);
                    //當(dāng)調(diào)用
                } else {
                    throw new RuntimeException("StateMachine.handleMessage: "
                            + "The start method not called, received msg: " + msg);
                }
                performTransitions(msgProcessedState, msg);

                // We need to check if mSm == null here as we could be quitting.
                if (mDbg && mSm != null) mSm.log("handleMessage: X");
            }
        }

SmHandler.handleMessage 函數(shù)主要執(zhí)行以下幾個(gè)操作:

  1. 根據(jù) mHasQuit 判斷是否退出,如果退出將不執(zhí)行后續(xù)指令
  2. 判斷是否初始完成(根據(jù)變量mIsConstructionCompleted),如果初始化完成調(diào)用 processMsg 將消息拋給當(dāng)前狀態(tài)執(zhí)行
  3. 如果尚未初始化涩惑,并且接受的是初始化命令 SM_INIT_CMD 將執(zhí)行一次初始化操作
  4. 當(dāng)命令執(zhí)行結(jié)束后仁期,執(zhí)行 performTransitions 函數(shù)用于轉(zhuǎn)變當(dāng)前狀態(tài)和 mStateStack

我們先接著上面第三個(gè)主題 [StateMachine] 初始化 看下第三步。 SM_INIT_CMD 指令的發(fā)出位于 SmHandler.completeConstruction 函數(shù)中:

//code SmHandler
 private final void completeConstruction() {
            ...
            /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
            sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
            ...
        }

處理初始化消息的時(shí)候會(huì)先將 mIsConstructionCompleted 設(shè)置為 true 竭恬,告訴狀態(tài)機(jī)已經(jīng)初始化過了跛蛋,可以讓狀態(tài)處理消息了。然后調(diào)用了個(gè) invokeEnterMethods 函數(shù)痊硕。這個(gè)函數(shù)的目的是回調(diào)當(dāng)前 mStateStack 棧中所有的活動(dòng)狀態(tài)的 enter 方法赊级。并且將非活躍狀態(tài)設(shè)置為活躍態(tài):

private final void invokeEnterMethods(int stateStackEnteringIndex) {
            for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
                mStateStack[i].state.enter();
                mStateStack[i].active = true;
            }
        }

這樣,如果我們的初始狀態(tài)是 STOP 的話岔绸,我們就可以在后臺(tái)打印中看到:

//console output:
output: [StateMachine] StopState enter

如果我們的初始狀態(tài)是 RUN 狀態(tài)的話就可以看到:

//console output:
output: [StateMachine] StopState enter
output: [StateMachine] CheckOilState enter
output: [StateMachine] RunState enter

上面就是處理初始化消息的過程此衅,到這一步,初始化的過程算是完整走完亭螟。我們繼續(xù)來看初始化后的邏輯,當(dāng)初始化已經(jīng)結(jié)束之后挡鞍,再收到的消息將通過 processMsg 函數(shù)提交給合適的狀態(tài)執(zhí)行。

private final State processMsg(Message msg) {
            StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
            //獲取當(dāng)前狀態(tài)節(jié)點(diǎn)
            if (isQuit(msg)) {
                //判斷當(dāng)前消息是否是退出消息
                transitionTo(mQuittingState);
            } else {
                while (!curStateInfo.state.processMessage(msg)) {
                //當(dāng)該狀態(tài)不處理當(dāng)前消息的時(shí)候预烙,將委托給父狀態(tài)處理
                    curStateInfo = curStateInfo.parentStateInfo;
                    if (curStateInfo == null) {
                        mSm.unhandledMessage(msg);
                        //當(dāng)沒有狀態(tài)可以處理當(dāng)前消息的時(shí)候回調(diào)unhandledMessage 
                        break;
                    }
                }
            }
            return (curStateInfo != null) ? curStateInfo.state : null;
        }

processMsg 會(huì)先判斷當(dāng)前是否是退出消息墨微,如果 isQuit 成立,將轉(zhuǎn)入 mQuittingState 狀態(tài)扁掸。我們將在后面分析如何執(zhí)行退出操作翘县,這塊東西,我們暫且有個(gè)印象谴分。當(dāng)并非退出消息時(shí)候锈麸,將會(huì)分配給當(dāng)前狀態(tài)處理,如果當(dāng)前狀態(tài)處理不了牺蹄,將委托給父狀態(tài)處理忘伞。比如當(dāng)前我們的初始狀態(tài)是 RUN 。那么對(duì)應(yīng)的 mStateStack 為:

[STOP,CHECK_OIL,RUN]

我們給狀態(tài)的測(cè)試代碼是:

private class BaseState extends State {
        @Override
        public void enter() {
            log(" enter "+this.getClass().getSimpleName());
            super.enter();

        }

        @Override
        public void exit() {
            log(" exit "+this.getClass().getSimpleName());
            super.exit();
        }
}

public class StopState extends BaseState {

        @Override
        public boolean processMessage(Message msg) {
            log("StopState.processMessage");
            return HANDLED;//處理消息
        }
    }

public class CheckOilState extends BaseState {
        @Override
        public boolean processMessage(Message msg) {
            log("CheckOilState.processMessage");
            return NOT_HANDLED;// 不處理消息
        }
}

public class RunState extends BaseState {}

我們往狀態(tài)機(jī) Car 發(fā)送一條消息:

Car car = new Car();
car.sendMessage(0x01);

我們將在后臺(tái)打印出log:

 --> enter StopState
 --> enter CheckOilState
 --> enter RunState
 // 初始化結(jié)束
 -->[StateMachine]:handleMessage 1
-->CheckOilState.processMessage // run狀態(tài)不處理,扔給checkoil狀態(tài)
-->StopState.processMessage // checkoil狀態(tài)不處理氓奈,扔給stop 狀態(tài)

當(dāng)然翘魄,如果你并不希望消息被委托調(diào)用,你可以在初始狀態(tài)調(diào)用 processMessage 函數(shù)的時(shí)候舀奶,返回 HANDLED 常量暑竟,這樣就不會(huì)往下調(diào)用。

5. [StateMachine] 狀態(tài)轉(zhuǎn)換

通常育勺,我們會(huì)在 State.processMessage 內(nèi)部但荤,通過調(diào)用 transitionTo 函數(shù)執(zhí)行一次狀態(tài)轉(zhuǎn)換,而調(diào)用這個(gè)函數(shù)只是將你要轉(zhuǎn)換的狀態(tài)存入一個(gè)臨時(shí)的對(duì)象中:

protected final void transitionTo(IState destState) {
        mSmHandler.transitionTo(destState);
}
private final void transitionTo(IState destState) {
            mDestState = (State) destState;
}

真正的狀態(tài)轉(zhuǎn)換將發(fā)生在 SmHandler.handleMessage 函數(shù)執(zhí)行之后:

 public final void handleMessage(Message msg) {
            if (!mHasQuit) {
                ...
                performTransitions(msgProcessedState, msg);//變更狀態(tài)
            }
        }

這里將調(diào)用 performTransitions 函數(shù)完成狀態(tài)轉(zhuǎn)換,假如,現(xiàn)在的狀態(tài)是 RUN 狀態(tài)涧至,當(dāng)需要轉(zhuǎn)成 ADD_OIL 狀態(tài)的時(shí)候腹躁,將進(jìn)行一下轉(zhuǎn)變:

/**
初始:
mStateStack : [ STOP,CHECK_OIL,RUN]
*/

private void performTransitions(State msgProcessedState, Message msg) {
            State orgState = mStateStack[mStateStackTopIndex].state;
            //orgState記錄當(dāng)前狀態(tài)
            State destState = mDestState;
            //destState 記錄要轉(zhuǎn)變的目標(biāo)狀態(tài)
            if (destState != null) {
                while (true) {
                    StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                    //查找跟目標(biāo)狀態(tài)的公共節(jié)點(diǎn)狀態(tài),此時(shí)為 STOP 狀態(tài)節(jié)點(diǎn)
                    invokeExitMethods(commonStateInfo);
                    //從棧頂一直到commonStateInfo(不包含) 所在的位置執(zhí)行退出操作
                    int stateStackEnteringIndex = moveTempStateStackToStateStack();
                    invokeEnterMethods(stateStackEnteringIndex);
                    moveDeferredMessageAtFrontOfQueue();
                    //將Deferred 消息放入隊(duì)列頭部優(yōu)先執(zhí)行
                    if (destState != mDestState) {
                        destState = mDestState;
                    } else {
                        break;
                    }
                }
                mDestState = null;
            }
            if (destState != null) {
                if (destState == mQuittingState) {
                    //TODO clean
                } else if (destState == mHaltingState) {
                    //TODO halt
                }
            }
        }

這段代碼執(zhí)行的時(shí)候化借,會(huì)先去尋找目標(biāo)節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)的公共祖先節(jié)點(diǎn)潜慎,這是通過調(diào)用 setupTempStateStackWithStatesToEnter 調(diào)用的捡多。StateMachine 的函數(shù)名起的見名知意蓖康,*Temp* 代表這個(gè)函數(shù)中要使用中間變量 mTempStateStack*ToEnter 代表需要對(duì)添加進(jìn)的狀態(tài)執(zhí)行 State.enter 操作垒手。

private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
            mTempStateStackCount = 0;//重置 mTempStateStack
            StateInfo curStateInfo = mStateInfo.get(destState);
            do {
                mTempStateStack[mTempStateStackCount++] = curStateInfo;
                curStateInfo = curStateInfo.parentStateInfo;
            } while ((curStateInfo != null) && !curStateInfo.active);
            //找到第一個(gè) active 的狀態(tài)節(jié)點(diǎn)蒜焊。
            return curStateInfo;
        }

setupTempStateStackWithStatesToEnter 函數(shù)就是將目標(biāo)節(jié)點(diǎn)的堆棧復(fù)制到 mTempStateStack 變量中,然后將最終相交的節(jié)點(diǎn)返回科贬。這里采用 do-while的寫法泳梆,說明這個(gè)函數(shù)的執(zhí)行,至少包含一個(gè) destState 元素榜掌。剛才從 RUN->ADD_OIL 的例子中优妙,setupTempStateStackWithStatesToEnter 將返回 STOP 狀態(tài),mTempStateStack 的為:

mTempStateStack: {ADD_OIL}

我們回到 performTransitions 的流程憎账,執(zhí)行 setupTempStateStackWithStatesToEnter 完套硼,將執(zhí)行 invokeExitMethods 函數(shù)。

private final void invokeExitMethods(StateInfo commonStateInfo) {
            while ((mStateStackTopIndex >= 0)
                    && (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
                State curState = mStateStack[mStateStackTopIndex].state;
                curState.exit();
                mStateStack[mStateStackTopIndex].active = false;
                mStateStackTopIndex -= 1;
            }
}

這個(gè)函數(shù)相當(dāng)于將 mStateStack 棧中的非 commonStateInfo 進(jìn)行出棧胞皱。

mStateStack: {STOP,CHECK_OIL,RUN} ->
invokeExitMethods(STOP) ->
mStateStack: {STOP}

執(zhí)行完出棧后邪意,只需要將我們剛才構(gòu)建的 mTempStateStack 拷貝到 mStateStack 就可以構(gòu)建新的狀態(tài)棧了,而這個(gè)操作是通過 moveTempStateStackToStateStack 函數(shù)完成反砌,而 moveTempStateStackToStateStack 我們剛才說過雾鬼,實(shí)際上就是將 mTempStateStack 逆序賦值到 mStateStack。這樣宴树,我們就構(gòu)建了一個(gè)新的 mStateStack:

mStateStack: {STOP,ADD_OIL}

這個(gè)時(shí)候策菜,我們構(gòu)建了一個(gè)新的狀態(tài)棧,相當(dāng)于已經(jīng)切換了狀態(tài)。performTransitions 在執(zhí)行完 moveTempStateStackToStateStack 之后做入,調(diào)用 invokeEnterMethods 函數(shù)冒晰,執(zhí)行非 active 狀態(tài)的 enter 方法。之后執(zhí)行 moveDeferredMessageAtFrontOfQueue 將通過 deferMessage 函數(shù)緩存的消息隊(duì)列放到 Handler 消息隊(duì)列的頭部:

...

                    int stateStackEnteringIndex = moveTempStateStackToStateStack();
                    invokeEnterMethods(stateStackEnteringIndex);
                    moveDeferredMessageAtFrontOfQueue();
                    //將Deferred 消息放入隊(duì)列頭部優(yōu)先執(zhí)行
                    if (destState != mDestState) {
                        destState = mDestState;
                    } else {
                        break;
                    }
                    
                    ...

當(dāng)我們完成狀態(tài)的轉(zhuǎn)換了以后竟块,需要對(duì)兩種特殊的狀態(tài)進(jìn)行處理壶运,在 performTransitions 函數(shù)的末尾會(huì)判斷兩個(gè)特殊的狀態(tài):

1. HaltingState
2. QuittingState

6. 狀態(tài)機(jī)的退出

狀態(tài)機(jī)的退出,StateMachine 提供了幾個(gè)方法:

  1. quit: 執(zhí)行完消息隊(duì)列中所有的消息后執(zhí)行退出和清理操作
  2. quitNow: 拋棄掉消息隊(duì)列中的消息浪秘,直接執(zhí)行退出和清理操作
  3. transitionToHaltingState: 拋棄掉消息隊(duì)列中的消息蒋情,直接執(zhí)行退出,不做清理

從上面的表述中看耸携,quit 相對(duì) halt 操作來說更加的安全棵癣。這個(gè) Threadinterceptstop 方法很類似,很好理解夺衍。上面我們說到狈谊,退出狀態(tài) HaltingStateQuittingState 是在performTransitions 函數(shù)的末尾判斷和執(zhí)行的,我們來看下代碼:

if (destState != null) {
                if (destState == mQuittingState) {
                    mSm.onQuitting();
                    cleanupAfterQuitting();//清理操作
                } else if (destState == mHaltingState) {
                    mSm.onHalting();//只是執(zhí)行回調(diào)
                }
            }
            
            
private final void cleanupAfterQuitting() {
            if (mSm.mSmThread != null) {
                getLooper().quit();//退出線程
                mSm.mSmThread = null;
            }
            /*清空數(shù)據(jù)*/
            mSm.mSmHandler = null;
            mSm = null;
            mMsg = null;
            mLogRecords.cleanup();
            mStateStack = null;
            mTempStateStack = null;
            mStateInfo.clear();
            mInitialState = null;
            mDestState = null;
            mDeferredMessages.clear();
            mHasQuit = true;
        }

當(dāng) destState == mQuittingState 語句成立沟沙,將回調(diào) StateMachine.onQuitting 函數(shù)河劝,之后將執(zhí)行 cleanupAfterQuitting 進(jìn)行清理操作。清理操作中矛紫,會(huì)將線程清空赎瞎,和其他數(shù)據(jù)變量清空,而如果 destState == mHaltingState 成立颊咬,StateMachine 將不執(zhí)行任何的清理操作务甥,通過回調(diào) onHalting 函數(shù)來通知狀態(tài)機(jī)退出。

7. 總結(jié)

Android 里面的這個(gè) StateMachine 狀態(tài)機(jī)在很多源碼中都有涉及喳篇,代碼也很簡單敞临,沒有什么太大的難度,希望以上的總結(jié)能幫各位看官理解 StateMachine 源碼的含義麸澜,并且能基于它挺尿,開發(fā)更多個(gè)性化的功能

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市痰憎,隨后出現(xiàn)的幾起案子票髓,更是在濱河造成了極大的恐慌,老刑警劉巖铣耘,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洽沟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蜗细,警方通過查閱死者的電腦和手機(jī)裆操,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門怒详,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人踪区,你說我怎么就攤上這事昆烁。” “怎么了缎岗?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵静尼,是天一觀的道長。 經(jīng)常有香客問我传泊,道長鼠渺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任眷细,我火速辦了婚禮拦盹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘溪椎。我一直安慰自己普舆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布校读。 她就那樣靜靜地躺著沼侣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪地熄。 梳的紋絲不亂的頭發(fā)上华临,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天芯杀,我揣著相機(jī)與錄音端考,去河邊找鬼。 笑死揭厚,一個(gè)胖子當(dāng)著我的面吹牛却特,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筛圆,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼裂明,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了太援?” 一聲冷哼從身側(cè)響起闽晦,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎提岔,沒想到半個(gè)月后仙蛉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碱蒙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年荠瘪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了夯巷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哀墓,死狀恐怖趁餐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情篮绰,我是刑警寧澤后雷,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站吠各,受9級(jí)特大地震影響喷面,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜走孽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一惧辈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧磕瓷,春花似錦盒齿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至硕盹,卻和暖如春符匾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瘩例。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國打工啊胶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人垛贤。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓焰坪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親聘惦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子某饰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,867評(píng)論 25 707
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司善绎,掛了不少黔漂,但最終還是拿到小米、百度禀酱、阿里炬守、京東、新浪比勉、CVTE劳较、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,218評(píng)論 11 349
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理驹止,服務(wù)發(fā)現(xiàn),斷路器观蜗,智...
    卡卡羅2017閱讀 134,638評(píng)論 18 139
  • 0x0.問題導(dǎo)入:weak與strong在控件定義時(shí)的糾結(jié) weak與strong在控件定義過程中時(shí)常用到臊恋,通常使...
    QuerySky閱讀 1,158評(píng)論 0 3
  • 01 話要走心要真誠 “說話是最容易的事抖仅,也是最難的事”,一個(gè)人張口說話砖第,真誠是最基本的要求撤卢。 說話的魅力并不在于...
    饑者求食閱讀 667評(píng)論 0 8