Android蓝牙源码分析——StateMachine状态机

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<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();

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的时候也是从下往上。不过也可以理解,初始化的时候是先从上开始,退出的时候是反着来。处理消息也是先让下处理,处理不好才往上走。

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/dingjikerbo/article/details/52238296
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞