天天看點

一文詳解 Android狀态機StateMachine 使用方式及實作原理

工作中有一同僚說到Android狀态機

StateMachine

。作為一名Android資深工程師,我居然沒有聽說過

StateMachine

,是以抓緊時間學習一下。

StateMachine

不是

Android SDK

中的相關API,其存在于

frameworks

層源碼中的一個Java類。可能因為如此,許多應用層的開發人員并未使用過。

是以這裡我們先說一下

StateMachine

的使用方式,然後再對源碼進行相關介紹。

  • StateMachine使用舉例
  • StateMachine原理學習

一、使用舉例

StateMachine 處于Android

frameworks

層源碼

frameworks/base/core/java/com/android/internal/util

路徑下。應用層若要使用

StateMachine

需将對應路徑下的三個類拷貝到自己的工程目錄下。

這三個類分别為:

StateMachine.java

State

IState

下邊是使用的代碼舉例,這個例子我也是網絡上找的(讀懂StateMachine源碼後,我對這個例子進行了一些簡單更改,以下為更改後的案例):

主要分以下幾個部分來說明:

  • PersonStateMachine.java案例代碼
  • PersonStateMachine 使用
  • 案例的簡單說明
  • 案例源碼下載下傳

1.1 PersonStateMachine

建立

PersonStateMachine

繼承

StateMachine

類。

建立四種狀态,四種狀态均繼承自

State

  • 預設狀态 BoringState
  • 工作狀态 WorkState
  • 吃飯狀态 EatState
  • 睡覺狀态 SleepState

定義了狀态轉換的四種消息類型:

  • 喚醒消息 MSG_WAKEUP
  • 困乏消息 MSG_TIRED
  • 餓了消息 MSG_HUNGRY
  • 狀态機停止消息 MSG_HALTING

下面來看完整的案例代碼:

public class PersonStateMachine extends StateMachine {

    private static final String TAG = "MachineTest";

    //設定狀态改變标志常量
    public static final int MSG_WAKEUP = 1; // 消息:醒
    public static final int MSG_TIRED = 2; // 消息:困
    public static final int MSG_HUNGRY = 3; // 消息:餓
    private static final int MSG_HALTING = 4; // 狀态機暫停消息

    //建立狀态
    private State mBoringState = new BoringState();// 預設狀态
    private State mWorkState = new WorkState(); // 工作
    private State mEatState = new EatState(); // 吃
    private State mSleepState = new SleepState(); // 睡

    /**
     * 構造方法
     *
     * @param name
     */
    PersonStateMachine(String name) {
        super(name);
        //加入狀态,初始化狀态
        addState(mBoringState, null);
        addState(mSleepState, mBoringState);
        addState(mWorkState, mBoringState);
        addState(mEatState, mBoringState);

        // sleep狀态為初始狀态
        setInitialState(mSleepState);
    }

    /**
     * @return 建立啟動person 狀态機
     */
    public static PersonStateMachine makePerson() {
        PersonStateMachine person = new PersonStateMachine("Person");
        person.start();
        return person;
    }


    @Override
    protected void onHalting() {
        synchronized (this) {
            this.notifyAll();
        }
    }


    /**
     * 定義狀态:無聊
     */
    class BoringState extends State {
        @Override
        public void enter() {
            Log.e(TAG, "############ enter Boring ############");
        }

        @Override
        public void exit() {
            Log.e(TAG, "############ exit Boring ############");
        }

        @Override
        public boolean processMessage(Message msg) {
            Log.e(TAG, "BoringState  processMessage.....");
            return true;
        }
    }

    /**
     * 定義狀态:睡覺
     */
    class SleepState extends State {
        @Override
        public void enter() {
            Log.e(TAG, "############ enter Sleep ############");
        }

        @Override
        public void exit() {
            Log.e(TAG, "############ exit Sleep ############");
        }

        @Override
        public boolean processMessage(Message msg) {
            Log.e(TAG, "SleepState  processMessage.....");
            switch (msg.what) {
                // 收到清醒信号
                case MSG_WAKEUP:
                    Log.e(TAG, "SleepState  MSG_WAKEUP");
                    // 進入工作狀态
                    transitionTo(mWorkState);
                    //...
                    //...
                    //發送餓了信号...
                    sendMessage(obtainMessage(MSG_HUNGRY));
                    break;
                case MSG_HALTING:
                    Log.e(TAG, "SleepState  MSG_HALTING");

                    // 轉化到暫停狀态
                    transitionToHaltingState();
                    break;
                default:
                    return false;
            }
            return true;
        }
    }


    /**
     * 定義狀态:工作
     */
    class WorkState extends State {
        @Override
        public void enter() {
            Log.e(TAG, "############ enter Work ############");
        }

        @Override
        public void exit() {
            Log.e(TAG, "############ exit Work ############");
        }

        @Override
        public boolean processMessage(Message msg) {
            Log.e(TAG, "WorkState  processMessage.....");
            switch (msg.what) {
                // 收到 餓了 信号
                case MSG_HUNGRY:
                    Log.e(TAG, "WorkState  MSG_HUNGRY");
                    // 吃飯狀态
                    transitionTo(mEatState);
                    //...
                    //...
                    // 發送累了信号...
                    sendMessage(obtainMessage(MSG_TIRED));
                    break;
                default:
                    return false;
            }
            return true;
        }
    }

    /**
     * 定義狀态:吃
     */
    class EatState extends State {
        @Override
        public void enter() {
            Log.e(TAG, "############ enter Eat ############");
        }

        @Override
        public void exit() {
            Log.e(TAG, "############ exit Eat ############");
        }

        @Override
        public boolean processMessage(Message msg) {
            Log.e(TAG, "EatState  processMessage.....");
            switch (msg.what) {
                // 收到 困了 信号
                case MSG_TIRED:
                    Log.e(TAG, "EatState  MSG_TIRED");
                    // 睡覺
                    transitionTo(mSleepState);
                    //...
                    //...
                    // 發出結束信号...
                    sendMessage(obtainMessage(MSG_HALTING));
                    break;
                default:
                    return false;
            }
            return true;
        }

    }
}           

1.2 PersonStateMachine 使用

// 擷取 狀态機引用
PersonStateMachine personStateMachine = PersonStateMachine.makePerson();
// 初始狀态為SleepState,發送消息MSG_WAKEUP
personStateMachine.sendMessage(PersonStateMachine.MSG_WAKEUP);           
  • SleepState

    狀态收到

    MSG_WAKEUP

    消息後,會執行對應狀态的

    processMessage

    方法
  • SleepState

    類中

    processMessage

    方法收到

    MSG_WAKEUP

    消息後,執行

    transitionTo(mWorkState)

    方法,完成狀态轉換。轉換到

    WorkState

    狀态。

1.3 案例簡單說明

幾種狀态的依賴關系如下:

一文詳解 Android狀态機StateMachine 使用方式及實作原理

構造方法中,添加所有狀态,并設定初始狀态:

PersonStateMachine(String name) {
    super(name);
    //加入狀态,初始化狀态
    addState(mBoringState, null);
    addState(mSleepState, mBoringState);
    addState(mWorkState, mBoringState);
    addState(mEatState, mBoringState);
    
    // sleep狀态為初始狀态
    setInitialState(mSleepState);
}           

通過以下方法,建立并啟動狀态機:

public static PersonStateMachine makePerson() {
    PersonStateMachine person = new PersonStateMachine("Person");
    person.start();
    return person;
}           

1.4 案例源碼下載下傳

Android_StateMachine案例位址

二、實作原理

StateMachine

中,開啟了一個線程

HandlerThread

,其對應的Handler為

SmHandler

。是以上文案例中對應狀态的

processMessage(Message msg)

方法,均在

HandlerThread

線程中執行。

2.1 從

StateMachine

的構造方法說起:

protected StateMachine(String name) {
    // 建立 HandlerThread
    mSmThread = new HandlerThread(name);
    mSmThread.start();
    // 擷取HandlerThread對應的Looper
    Looper looper = mSmThread.getLooper();
    // 初始化 StateMachine
    initStateMachine(name, looper);
}           
  • StateMachine

    的構造方法中,建立并啟動了一個線程

    HandlerThread

  • initStateMachine

    方法中,建立了

    HandlerThread

    線程對應的Handler

    SmHandler

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

    構造方法中,向狀态機中添加了兩個狀态:一個狀态為狀态機的

    暫停狀态mHaltingState

    、一個狀态為狀态機的

    退出狀态mQuittingState

private SmHandler(Looper looper, StateMachine sm) {
    super(looper);
    mSm = sm;

    // 添加狀态:暫停 和 退出
    // 這兩個狀态 無父狀态
    addState(mHaltingState, null);
    addState(mQuittingState, null);
}           
  • mHaltingState

    狀态,顧名思義讓狀态機暫停,其對應的

    processMessage(Message msg)

    方法,傳回值為true,将消息消費掉,但不處理消息。進而使狀态機狀态停頓到

    mHaltingState

    狀态
  • mQuittingState

    狀态,若進入該狀态, 狀态機将退出。

    HandlerThread

    線程對應的Looper将退出,

    HandlerThread

    線程會被銷毀,所有加入到狀态機的狀态被清空。

2.2 狀态機的start() 方法

狀态機的初始化說完,下邊來說狀态機的啟動方法

start()

public void start() {
    // mSmHandler can be null if the state machine has quit.
    SmHandler smh = mSmHandler;
    // StateMachine 未進行初始化,為什麼不抛出一個異常
    if (smh == null) {
        return;
    }
    // 完成狀态機建設
   smh.completeConstruction();
}           
  • 從以上代碼可以看到,其中隻有一個方法

    completeConstruction()

    ,用于完成狀态機的建設。
private final void completeConstruction() {
    int maxDepth = 0;
    // 循環判斷所有狀态,看看哪一個鍊最長,得出深度
    for (StateInfo si : mStateInfoHashMap.values()) {
        int depth = 0;
        for (StateInfo i = si; i != null; depth++) {
            i = i.parentStateInfo;
        }
        if (maxDepth < depth) {
            maxDepth = depth;
        }
    }
    // 狀态堆棧
    mStateStack = new StateInfo[maxDepth];
    // 臨時狀态堆棧
    mTempStateStack = new StateInfo[maxDepth];
    // 初始化堆棧
    setupInitialStateStack();

    // 發送初始化完成的消息(消息放入到隊列的最前邊)
    sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
}           
  • maxDepth

    是狀态機中,最長依賴鍊的長度。
  • mStateStack

    mTempStateStack

    為兩個用數組實作的堆棧。這兩個堆棧的最大長度,即為

    maxDepth

    。其用來存儲

    目前活躍狀态

    目前活躍狀态的父狀态、父父狀态、...等

  • setupInitialStateStack();

    完成狀态的初始化,将目前的活躍狀态放入到

    mStateStack

    堆棧中。

下邊來具體說

setupInitialStateStack();

方法中,如何完成棧的初始化。

private final void setupInitialStateStack() {
    // 擷取初始狀态資訊
    StateInfo curStateInfo = mStateInfoHashMap.get(mInitialState);
    //
    for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
        // 初始狀态 放入臨時堆棧
        mTempStateStack[mTempStateStackCount] = curStateInfo;
        // 目前狀态的 所有父狀态 一級級放入堆棧
        curStateInfo = curStateInfo.parentStateInfo;
    }

    // 清空 狀态堆棧
    // Empty the StateStack
    mStateStackTopIndex = -1;
    // 臨時堆棧 換到 狀态堆棧
    moveTempStateStackToStateStack();
}           
  • 拿案例中狀态來舉例,将

    初始化狀态

    放入

    mTempStateStack

    堆棧中
  • 初始化狀态

    父狀态

    父父狀态

    父父父狀态

    ... 都一一放入到mTempStateStack堆棧中
一文詳解 Android狀态機StateMachine 使用方式及實作原理
  • 然後moveTempStateStackToStateStack()方法中,

    mTempStateStack

    出棧,

    mStateStack

    入棧,将所有狀态資訊導入到

    mStateStack

    堆棧,并清空

    mTempStateStack

    堆棧。
一文詳解 Android狀态機StateMachine 使用方式及實作原理

到這裡,初始化基本完成,但我們還落下一部分代碼沒有說:

// 發送初始化完成的消息(消息放入到隊列的最前邊)
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));           
  • 發送一個初始化完成的消息到

    SmHandler

    當中。

下邊來看一下

SmHandler

handleMessage(Message msg)

方法:

public final void handleMessage(Message msg) {

    // 處理消息
    if (!mHasQuit) {
        // 儲存傳入的消息
        mMsg = msg;
        State msgProcessedState = null;
        // 已完成初始化
        if (mIsConstructionCompleted) {
        // ..
        }
        // 接收到 初始化完成的消息
        else if (!mIsConstructionCompleted
                && (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
            /** Initial one time path. */
            // 初始化完成
            mIsConstructionCompleted = true;
            // 調用堆棧中狀态的enter方法,并将堆棧中的狀态設定為活躍狀态
            invokeEnterMethods(0);
        } else {
        // ..
        }
        // 執行Transition
        performTransitions(msgProcessedState, msg);
    }
}           
  • 接收到初始化完成的消息後

    mIsConstructionCompleted = true;

    對應的标志位變過來
  • 執行

    invokeEnterMethods

    方法将

    mStateStack

    堆棧中的所有狀态設定為活躍狀态,并由

    父—>子

    的順序,執行堆棧中狀态的

    enter()

  • performTransitions(msgProcessedState, msg);

    在start()時,其中的内容全部不執行,是以先不介紹。

invokeEnterMethods

方法的方法體如下:

private final void invokeEnterMethods(int stateStackEnteringIndex) {
    for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
        if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
        mStateStack[i].state.enter();
        mStateStack[i].active = true;
    }
}           
  • 可以看到,其将

    mStateStack

    父—>子

    enter()

到此start()完成,最終

mStateStack

堆棧狀态,也如上圖所示。

2.3 狀态轉化

還是拿案例中的代碼舉例:

// 擷取 狀态機引用
PersonStateMachine personStateMachine = PersonStateMachine.makePerson();
// 初始狀态為SleepState,發送消息MSG_WAKEUP
personStateMachine.sendMessage(PersonStateMachine.MSG_WAKEUP);           
  • 通過調用

    sendMessage(PersonStateMachine.MSG_WAKEUP);

    方法,向

    SmHandler

    中發送一個消息,來觸發狀态轉化。
  • 可以說

    sendMessage(PersonStateMachine.MSG_WAKEUP);

    消息,為狀态轉化的導火索。

下邊,再次看一下

SmHandler

handleMessage(Message msg)

public final void handleMessage(Message msg) {
    // 處理消息
    if (!mHasQuit) {
        // 儲存傳入的消息
        mMsg = msg;
        State msgProcessedState = null;
        // 已完成初始化
        if (mIsConstructionCompleted) {
            // 處理消息的狀态
            msgProcessedState = processMsg(msg);
        }
        // 接收到 初始化完成的消息
        else if (!mIsConstructionCompleted
                && (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
            // 初始化完成
            mIsConstructionCompleted = true;
            // 調用堆棧中狀态的enter方法,并将堆棧中的狀态設定為活躍狀态
            invokeEnterMethods(0);
        } else {
            throw new RuntimeException("StateMachine.handleMessage: "
                    + "The start method not called, received msg: " + msg);
        }
        // 執行Transition
        performTransitions(msgProcessedState, msg);
    }
}           
  • 因為初始化已經完成,代碼會直接走到

    processMsg(msg);

    方法中。

我們來看

processMsg(msg);

private final State processMsg(Message msg) {
    // 堆棧中找到目前狀态
    StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
    // 是否為退出消息
    if (isQuit(msg)) {
        // 轉化為退出狀态
        transitionTo(mQuittingState);
    } else {
        // 狀态傳回true 則是可處理此狀态
        // 狀态傳回false 則不可以處理
        while (!curStateInfo.state.processMessage(msg)) {
            // 目前狀态的父狀态
            curStateInfo = curStateInfo.parentStateInfo;
            // 父狀态未null
            if (curStateInfo == null) {
                // 回調到未處理消息方法中
                mSm.unhandledMessage(msg);
                break;
            }
        }
    }
    // 消息處理後,傳回目前狀态資訊
    // 如果消息不處理,則傳回其父狀态處理,傳回處理消息的父狀态
    return (curStateInfo != null) ? curStateInfo.state : null;
}           
  • 代碼會直接走到

    while (!curStateInfo.state.processMessage(msg))

mStateStack

堆棧中,最上層狀态的

processMessage(msg)

方法。案例中這個狀态為

SleepState

  • 這裡

如果

mStateStack

堆棧中狀态的processMessage(msg)方法傳回true,則表示其消費掉了這個消息;

如果其傳回false,則表示不消費此消息,那麼該消息将繼續向其

父狀态

進行傳遞;

  • 最終将傳回,消費掉該消息的狀态。

這裡,堆棧對上層的狀态為

SleepState

。是以我們看一下其對應的

processMessage(msg)

方法。

public boolean processMessage(Message msg) {
    switch (msg.what) {
        // 收到清醒信号
        case MSG_WAKEUP:
            // 進入工作狀态
            transitionTo(mWorkState);
            //...
            //...
            //發送餓了信号...
            sendMessage(obtainMessage(MSG_HUNGRY));
            break;
        case MSG_HALTING:
        // ...
            break;
        default:
            return false;
    }
    return true;
}           
  • 在SleepState狀态的

    processMessage(Message msg)

    方法中,其收到

    MSG_WAKEUP

    消息後,會調用

    transitionTo(mWorkState);

    方法,将目标狀态設定為

    mWorkState

我們看一下

transitionTo(mWorkState);

private final void transitionTo(IState destState) {
    mDestState = (State) destState;
}           
  • 可以看到,

    transitionTo(IState destState)

    方法,隻是一個簡單的狀态指派。

下邊我們回到

SmHandler

handleMessage(Message msg)

  • 代碼會執行到

    SmHandler.handleMessage(Message msg)

    performTransitions(msgProcessedState, msg);

    方法之中。
  • 而這裡我們傳入的參數

    msgProcessedState

    mSleepState

private void performTransitions(State msgProcessedState, Message msg) {
    // 目前狀态
    State orgState = mStateStack[mStateStackTopIndex].state;
    // ...
    // 目标狀态
    State destState = mDestState;
    if (destState != null) {
        while (true) {
            // 目标狀态 放入temp 堆棧
            // 目标狀态的 父狀态 作為參數 傳入下一級
            StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
            // commonStateInfo 狀态的子狀态全部退棧
            invokeExitMethods(commonStateInfo);
            // 目标狀态入棧
            int stateStackEnteringIndex = moveTempStateStackToStateStack();
            // 入棧狀态 活躍
            invokeEnterMethods(stateStackEnteringIndex);
            //...
            moveDeferredMessageAtFrontOfQueue();

            if (destState != mDestState) {
                // A new mDestState so continue looping
                destState = mDestState;
            } else {
                // No change in mDestState so we're done
                break;
            }
        }
        mDestState = null;
    }
    // ...
}           
  • 以上方法中 傳入的參數

    msgProcessedState

    mSleepState

  • 方法中

    destState

    目标狀态為

    mWorkState

此時此刻

performTransitions(State msgProcessedState, Message msg)

方法中内容的執行示意圖如下:

A 目标狀态放入到mTempStateStack隊列中

// 目标狀态 放入temp 堆棧
// 目标狀态的 父狀态 作為參數 傳入下一級
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);           
  • 1、将

    WorkState

    狀态放入到

    mTempStateStack

  • 2、将

    WorkState

    狀态的

    非活躍父狀态

    一一入

    mTempStateStack

    堆棧
  • 3、因為

    WorkState

    狀态的父狀态為

    BoringState

    ,是活躍狀态,是以隻将

    WorkState

    放入到

    mTempStateStack

  • 4、傳回活躍的父狀态

    BoringState

以上代碼的執行示意圖如下:

一文詳解 Android狀态機StateMachine 使用方式及實作原理

B

commonStateInfo

狀态在

mStateStack

堆棧中的子狀态退堆棧

commonStateInfo

setupTempStateStackWithStatesToEnter(destState);

方法的傳回參數。這裡是

BoringState

// commonStateInfo 狀态的子狀态全部退棧
invokeExitMethods(commonStateInfo);           
  • 1、

    BoringState

    作為參數傳入到

    invokeExitMethods(commonStateInfo);

  • 2、其方法内容為,将

    BoringState

    全部子狀态退堆棧

一文詳解 Android狀态機StateMachine 使用方式及實作原理

C

mTempStateStack

全部狀态出堆棧,

mStateStack

入堆棧

// 目标狀态入棧
int stateStackEnteringIndex = moveTempStateStackToStateStack();
// 入棧狀态 活躍
invokeEnterMethods(stateStackEnteringIndex);           
  • moveTempStateStackToStateStack

    方法中:

    mTempStateStack

    mStateStack

  • invokeEnterMethods(stateStackEnteringIndex);方法中,将新加入的狀态設定為

    活躍狀态

    ;并調用其對應的

    enter()

最終的堆棧狀态為:

一文詳解 Android狀态機StateMachine 使用方式及實作原理

到此StateMachine的源碼講解完成。

感興趣的同學,還是自己讀一遍源碼吧,希望我的這篇文章可以為你的源碼閱讀提供一些幫助。

= THE END =

文章首發于公衆号”CODING技術小館“,如果文章對您有幫助,可關注我的公衆号。

繼續閱讀