状态机源码分析

状态机源码

com.android.internal.util.StateMachine

状态机初始化

状态机源码分析_第1张图片
image.png

HandlerThread、SmHandler,Loop使用子线程loop。

状态机树状结构

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

状态添加过程其实就是为每个State创建相应的StateInfo对象,通过该对象来建立各个状态之间的关系,并且一个State-StateInfo键值对的方式保存到mStateInfo Hash表中。除了根节点之外,每个节点状态都包含自己的parentState,从形状上来看,很像二叉树的结构,如图所示:

状态机源码分析_第2张图片
image.png

启动状态机

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

这个方法用来设置状态机的初始化状态,你可以理解为是个目标状态,在执行start方法后,状态机会流转到这个状态下。

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];
    //设置状态堆栈
    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中取得初始状态mInitialState对应的StateInfo  
    StateInfo curStateInfo = mStateInfo.get(mInitialState);  
    //从初始状态mInitialState开始根据父子关系填充mTempStateStack堆栈  
    for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {  
        mTempStateStack[mTempStateStackCount] = curStateInfo;  
        curStateInfo = curStateInfo.parentStateInfo;  
    }  
    // Empty the StateStack  
    mStateStackTopIndex = -1;  
    //将mTempStateStack中的状态按反序方式移动到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方法是根据初始化状态以及树状结构关系填充mTempStateStack,可以理解为把当前状态到根节点的状态对象保存在mTempStateStack列表中。

mStateStackTopIndex表示栈顶的index,这个值会随着mStateStack的入栈和出栈发生变化,而moveTempStateStackToStateStack方法是把mTempStateStack中的状态反序填充到mStateStack中,这里只是将mStateStack不包含的新状态填充进来,并不是整体反序填充,所以要看mStateStack在填充之前的状态如何。

举个栗子:

假如目标状态为S4,则mTempStateStack内的状态依次为S4,S1,S0,若mStateStack为空,则全量反序填充,若mStateStack的栈顶为S1,则只会填充S4,并且startIndex为2。当然,初始化的时候mStateStack肯定为空,等下分析状态切换时的情况。

@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");
}

当栈状态初始化完成后执行了sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj)),如上段代码所示,这句话会走到第二个if条件,其实就是调用了各个状态的enter方法,并置mIsConstructionCompleted为false表示初始化完成,后续这个if条件不会再调用。

消息处理

sendMessage

StateMachine的sendMessage等一系列方法,都是用来做消息处理的,调用到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);
            }
        }
    }
}

这个方法是从当前状态向根节点循环遍历,对当前消息进行消化,如果返回true,代表当前状态已处理该消息,否则交给父状态处理。

这里有必要提到state的父类,所有的状态对象都包含重要的三个方法,分别为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;
}

其中前两个方法是用来做状态流转的,最后一个方法是用来做消息处理的。注意这三个方法都是在HandlerThread线程中调用的,不能直接操作ui。

状态切换

状态切换都发生在performTransitions();方法中,也就是说在每一次消息处理之后,就会进行状态的切换,当然有可能切换也有可能不切换,具体看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对象代表目标状态,正常情况下是空的,也就不会进行状态的切换,当调用transitionTo方法,代表设置了目标状态,这时候就会进入循环。这段代码的核心在这几行:

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方法是基于目标状态destState建立一个mTempStateStack列表,注意setupTempStateStackWithStatesToEnter方法的whie循环,在while循环时判断了active的状态,而且使用的do while循环,这个方法会让它找到交叉点的位置,并且包含交叉点,而如果没有交叉点,就会返回另一棵树的根节点。

举个栗子:

假设当前栈为S0,S1,S3,这时候我要切换到目标状态S4时,这个方法建立的mTempStateStack列表为S4,S1,而返回的正是S1。

这时候invokeExitMethods方法刚好倒序遍历当前mStateStack并依次退栈,直到命中交叉点为止,否则就全部退栈。

这个特性说明,每个状态的enter和exit方法只执行一次,当状态切换时,路径上已经存在并激活的状态,不会重新走exit和enter方法。

最后调用invokeEnterMethods方法,会把从交叉点到目标节点的所有状态的enter方法按顺序调用一遍。

以上就完成了这个状态机的流转过程。

你可能感兴趣的:(状态机源码分析)