Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> StateMachine源碼分析

StateMachine源碼分析

編輯:關於Android編程

StateMachine有自己單獨的工作線程,

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

    initStateMachine(name, looper);
}

StateMachine中一個很重要的角色就是SmHandler,SmHandler在構造伊始就添加了兩個狀態:

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

    addState(mHaltingState, null);
    addState(mQuittingState, null);
}

這兩個狀態意思是整個狀態機的停止狀態和退出狀態,如下:

/**
 * State entered when transitionToHaltingState is called.
 */
private class HaltingState extends State {
    @Override
    public boolean processMessage(Message msg) {
        mSm.haltedProcessMessage(msg);
        return true;
    }
}

/**
 * State entered when a valid quit message is handled.
 */
private class QuittingState extends State {
    @Override
    public boolean processMessage(Message msg) {
        return NOT_HANDLED;
    }
}

可見這兩個狀態都是繼承自State,這個狀態機中的狀態都是用State表示的,如下:

public class State implements IState {

    protected State() {
    }

    @Override
    public void enter() {
    }

    @Override
    public void exit() {
    }

    @Override
    public boolean processMessage(Message msg) {
        return false;
    }

    @Override
    public String getName() {
        String name = getClass().getName();
        int lastDollar = name.lastIndexOf('$');
        return name.substring(lastDollar + 1);
    }
}

看起來很簡單,主要就三個函數,enter表示進入狀態的回調,exit表示離開狀態的回調,processMessage表示收到消息的回調。

再來看State是如何添加到狀態機中的,如下:

/** The map of all of the states in the state machine */
private HashMap mStateInfo = new HashMap();

private final StateInfo addState(State state, State parent) {
    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;

    return stateInfo;
}

可見每個State都只能有最多一個parent,或者沒有parent,那這種帶層級的State狀態機有什麼意義呢?因為以往我們認為的狀態機都是若干完全獨立的狀態之間互相切換,不會有狀態層級關系的,接下來我們就來看看這種層級關系的奧秘,從狀態切換入手:

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

只是設置了一個變量,不免讓人有些失望,我們看這個變量在哪引用的,結果是在performTransitions中,而這個函數是在SmHandler的handleMessage中:

/** true if construction of the state machine has not been completed */
private boolean mIsConstructionCompleted;

@Override
public final void handleMessage(Message msg) {
    if (!mHasQuit) {
        /** Save the current message */
        mMsg = msg;

        /** State that processed the message */
        State msgProcessedState = null;
        if (mIsConstructionCompleted) {
            /** Normal path */
            msgProcessedState = 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(msgProcessedState, msg);
    }
}

這個mHasQuit從字面意思上就是狀態機quit了,狀態切換到QuitState時整個狀態機就要destroy了,這時候再發消息也不會處理了。再看mIsConstructionCompleted,這個字面意思是狀態機的初始化構造是否結束了,用到的地方也就是handleMessage中,而構造指令就是SM_INIT_CMD,發出指令的地方在狀態機啟動的時候,如下:

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();
}

我們參考AdapterState狀態機,如下:

public static AdapterState make(AdapterService service, AdapterProperties adapterProperties) {
    Log.d(TAG, "make() - Creating AdapterState");
    AdapterState as = new AdapterState(service, adapterProperties);
    as.start();
    return as;
}

就是創建好狀態機之後調用其start函數啟動狀態機。我們接下來看看start函數中completeConstruction的實現,如下:

private final void completeConstruction() {
    /**
     * 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;
        }
    }

    mStateStack = new StateInfo[maxDepth];
    mTempStateStack = new StateInfo[maxDepth];
    setupInitialStateStack();

    /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
    sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
}

這裡首先遍歷所有的狀態,算出最大的深度,然後初始化狀態棧mStateStack和mTempStateStack,再調用setupInitialStateStack,最後才發送了SM_INIT_CMD到消息隊列的頭。

狀態機的切換中非常重要的角色就是這個狀態棧了,所以我們要重點關注狀態棧的初始化。不過這裡還是先分析SM_INIT_CMD,發消息的時候還帶上了mSmHandlerObj,這個東西就是一個普通的Object,每次狀態機自身發的消息都會帶上這個Object以區分是外面的消息還是狀態機自身的消息,比如關於INIT和QUIT都會帶上這個Object。在handleMessage中收到SM_INIT_CMD後會給mIsConstructionCompleted置為true,表示初始化過了,然後調用invokeEnterMethods(0),如下:

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

這個函數會從stateStackEnteringIndex到mStateStackTopIndex遍歷調用enter函數,這個mStateStackTopIndex是什麼呢?要搞清楚這個問題我們就得回到setupInitialStateStack函數了。

private final void setupInitialStateStack() {
    StateInfo curStateInfo = mStateInfo.get(mInitialState);
    for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
        mTempStateStack[mTempStateStackCount] = curStateInfo;
        curStateInfo = curStateInfo.parentStateInfo;
    }

    // Empty the StateStack
    mStateStackTopIndex = -1;

    moveTempStateStackToStateStack();
}

這裡從initial state開始遍歷其父state一直到頭,遍歷路徑記錄在mTempStateStack中,而將StateStack棧頂index置為-1,然後moveTempStateStackToStateStack。

private final int moveTempStateStackToStateStack() {
    int startingIndex = mStateStackTopIndex + 1;
    int i = mTempStateStackCount - 1;
    int j = startingIndex;
    while (i >= 0) {
        mStateStack[j] = mTempStateStack[i];
        j += 1;
        i -= 1;
    }

    mStateStackTopIndex = j - 1;
    return startingIndex;
}

這個函數相當於將TempStateStack倒序copy到StateStack中。在TempStateStack中先入棧的是底層的狀態,後入棧的是頂層的狀態,而StateStack剛好相反,先入棧的是頂層parent狀態,後入棧的是底層狀態。現在我們回到invokeEnterMethods(0),這裡會從StateStack的0開始到mStateStackTopIndex調用enter,也就是從狀態機的初始狀態的最頂層parent層層往下調到最底層狀態的enter。

總結一下,狀態機初始化時會先addState,然後設置好initial state,然後start,在start中初始化StateStack,將initial state從祖先開始依次入棧,然後再從祖先開始依次調用他們的enter回調。

我們再回到handleMessage,在invokeEnterMethods之後還調用了performTransitions(msgProcessedState, msg); 這個是檢查狀態切換的,如果設置過mDestState則這裡要切換狀態了:

private void performTransitions(State msgProcessedState, Message msg) {
    State destState = mDestState;
    if (destState != null) {
        /**
         * Process the transitions including transitions in the enter/exit methods
         */
        while (true) {
            /**
             * 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();

            if (destState != mDestState) {
                // A new mDestState so continue looping
                destState = mDestState;
            } else {
                // No change in mDestState so we're done
                break;
            }
        }
        mDestState = null;
    }

    /**
     * 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();
        }
    }
}

由於是初始化,所以msgProcessedState為null,而且mDestState也為null,所以這裡其實什麼也沒有做。再回到handleMessage,如果是初始化之後則會調processMsg,如下

private final State processMsg(Message msg) {
    StateInfo curStateInfo = mStateStack[mStateStackTopIndex];

    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;
            }
        }
    }
    return (curStateInfo != null) ? curStateInfo.state : null;
}

從狀態棧中取出棧頂狀態,將msg交給該state處理,如果處理了返回true則直接返回當前處理的state,否則交給父state處理,如果一直沒有哪個state可以處理的話就調用SmHandler的unhandledMessage。

從processMsg返回到handleMessage後,還會調用performTransitions,不過可以看到如果mDestState為空則什麼也不做。而mDestState是調transitionTo設置的,所以如果是單純的發消息不會涉及狀態的切換。

通常transitionState都是在某個state的processMsg中,這樣在processMsg返回後繼續調performTransitions時就會檢查切換狀態了。

不過注意的是切換狀態會依次將當前狀態棧出棧並將新狀態鏈入棧,不過如果兩者有共同的祖先結點,那祖先結點就沒必要折騰了,只是下面不同的子狀態才exit。我們分析performTransitions函數,首先通過setupTempStateStackWithStatesToEnter找到最低公共祖先,這裡面就是從目標state開始往上遍歷直到發現state是active為止,因為當前state的鏈上肯定都是active的。

接下來從當前狀態開始調用exit直到最低公共祖先,注意不包括這個祖先,路上的state的active都標為false。

我們總結一下,當調enter的時候是從上往下,調exit的時候是從下往上,處理msg的時候也是從下往上。不過也可以理解,初始化的時候是先從上開始,退出的時候是反著來。處理消息也是先讓下處理,處理不好才往上走。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved