Android状态机源码分析

之前有人问过我状态机的工作原理,一直比较忙,没来的及分析源码,现在挤出点时间来好好分析一下。

在Android系统中,经常使用状态机来处理不同状态下的行为动作。状态机是将对象的状态与行为封装在一起;可以解决庞大的分支语句带来程序阅读性差和不便于进行扩展问题,使整个结构变得更加清晰明了,降低程序管理的复杂性提高灵活度。Android系统的StateMachine机制是一个State模式的应用,StateMachine是一个分层处理消息的状态机,并且是能够有分层排列状态。有不懂State设计模式到同学可以去看下State模式,有助于理解本次分析。

下面先来看一张图
Android状态机源码分析_第1张图片

构造状态机
StateMachine的构造函数都是protected类型,不能实例化,都是由其子类进行初始化操作。StateMachine有三个重载的构造函数,一个是通过指定消息循环对象来构造状态机

    /**
     * Constructor creates a StateMachine using the looper.
     *
     * @param name of the state machine
     */
    protected StateMachine(String name, Looper looper) {
        initStateMachine(name, looper);
    }

另一个则是直接启动一个消息循环线程来构造一个状态机

    /**
     * Constructor creates a StateMachine with its own thread.
     *
     * @param name of the state machine
     */
    protected StateMachine(String name) {
        mSmThread = new HandlerThread(name);
        mSmThread.start();
        Looper looper = mSmThread.getLooper();

        initStateMachine(name, looper);
    }

还有一个是

    /**
     * Constructor creates a StateMachine using the handler.
     *
     * @param name of the state machine
     */
    protected StateMachine(String name, Handler handler) {
        initStateMachine(name, handler.getLooper());
    }

这几个构造函数都会通过initStateMachine函数来初始化该状态机。

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

初始化过程比较简单,就是将状态机名称保存到成员变量mName中,同时创建SmHandler对象,SmHandler是一个Handle对象,用于派发消息。
Android状态机源码分析_第2张图片
状态机中的每个状态使用State来封装,对于每个状态的信息又采用StateInfo来描述
Android状态机源码分析_第3张图片
StateInfo是一个内部类,主要就3个成员变量:state,parentStateInfo, 和状态active


        /**
         * Information about a state.
         * Used to maintain the hierarchy.
         */
        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;

            /**
             * Convert StateInfo to string
             */
            @Override
            public String toString() {
                return "state=" + state.getName() + ",active=" + active + ",parent="
                        + ((parentStateInfo == null) ? "null" : parentStateInfo.state.getName());
            }
        }

S
tateMachine三个内部类:
1.ProcessedMessageInfo:保存已处理消息的信息;
2.ProcessedMessages:存储StateMachine最近处理的一些消息,需要保存最近处理的消息条数默认20,可以用户自己设定最大数目;
3.SmHandle是消息处理派发和状态控制切换的核心,运行在单独的线程上;

mStateStack和mTempStateStack是一个数组栈,用于保存状态机中的链式状态关系。

        /** Stack used to manage the current hierarchy of states */
        private StateInfo mStateStack[];
        /** A temporary stack used to manage the state stack */
        private StateInfo mTempStateStack[];

mStateInfo定义为一个Hash链表,用于保存添加的所有状态。mInitialState保存初始状态,mDestState保存切换的目的状态。

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

        /** The initial state that will process the first message */
        private State mInitialState;

        /** The destination state when transitionTo has been invoked */
        private State mDestState;

建立树形层次结构状态机
在构造完一个状态机前需要向状态机中添加各种状态,StateMachine提供了addState函数来添加状态

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

直接通过mSmHandler来完成状态添加过程

        /**
         * Add a new state to the state machine. Bottom up addition
         * of states is allowed but the same state may only exist
         * in one hierarchy.
         *
         * @param state the state to add
         * @param parent the parent of state
         * @return stateInfo for this state
         */
        private final StateInfo addState(State state, State parent) {
            if (mDbg) {
                mSm.log("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) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
            return stateInfo;
        }

状态添加过程其实就是为每个State创建相应的StateInfo对象,通过该对象来建立各个状态之间的关系,并且一个State-StateInfo键值对的方式保存到mStateInfo Hash表中。StateInfo就是包装State组成一个Node,建立State的父子关系;mStateInfo =new HashMap

sm.addState(S0,null);
sm.addState(S1,S0);
sm.addState(S2,S0);
sm.addState(S3,S1);
sm.addState(S4,S1);
sm.addState(S5,S2);
sm.addState(S6,S2);
sm.addState(S7,S2);
setInitialState(S4);      //设置初始状态

1.添加根节点状态过程
对于根节点状态的加入sm.addState(S0,null)代码执行如下:

private final StateInfo addState(S0,null) {
    StateInfo parentStateInfo = null;
    //状态S0还未加入状态机中
    StateInfo stateInfo = mStateInfo.get(state);
  if (stateInfo == null) {
    //创建State详细信息对象
    stateInfo = new StateInfo();
        //将S0加入状态机中
    mStateInfo.put(state, stateInfo);
  }
  //设置状态S0的状态信息
    stateInfo.state = state;
    //S0的父节点为null
    stateInfo.parentStateInfo = parentStateInfo;
  stateInfo.active = false;
  return stateInfo;
}

2.添加树枝节点状态过程
对于子节点S1状态的加入过程如下sm.addState(S1,S0):

private final StateInfo addState(State state, State parent) {
  StateInfo parentStateInfo = null;
    //状态S0在前面已经加入状态机中了
  if (parent != null) {
    //获取S0详细信息 StateInfo
        parentStateInfo = mStateInfo.get(parent);
        //因为S0已经存在于状态机中,因此这里不为空
    if (parentStateInfo == null) {
      //当前状态父状态未加入到StateMachine中,递归先加入其父State
      parentStateInfo = addState(parent, null);
    }
  }
  //从状态机中得到S1的状态信息,由于S1还为加入状态机,因此得到的结果为空
  StateInfo stateInfo = mStateInfo.get(state);
  if (stateInfo == null) {
    //创建State详细信息对象
    stateInfo = new StateInfo();
        //将S1加入状态机中
    mStateInfo.put(state, stateInfo);
  }
  //S1的状态信息刚创建,还没有为其设置父节点,因此其父节点为空
  if ((stateInfo.parentStateInfo != null) &&
    (stateInfo.parentStateInfo != parentStateInfo)) {
      throw new RuntimeException("state already added");
  }
  //设置状态S1的状态信息
    stateInfo.state = state;
    //S1的父节点为S0
    stateInfo.parentStateInfo = parentStateInfo;
  stateInfo.active = false;
  return stateInfo;
}

对于其他树枝节点的添加过程类似,这里不在介绍,最后保存在mStateInfo表中的所有状态之间就建立了以下树形关系:
Android状态机源码分析_第4张图片

启动状态机

当向状态机中添加完所有状态时,通过函数start函数来启动状态机

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

调用mSmHandler的completeConstruction函数来完成状态机的构造完成处理

private final void completeConstruction() {
    if (mDbg) Log.d(TAG, "completeConstruction: E");
    //查找状态树的深度
    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,mTempStateStack状态栈
    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");
}

计算状态树的最大深度方法:
1.遍历状态树中的所有节点;
2.以每一个节点为起始点,根据节点父子关系查找到根节点;
3.累计起始节点到根节点之间的节点个数;查找最大的节点个数。
根据查找到的树的最大节点个数来创建两个状态堆栈,并调用函数setupInitialStateStack来填充该堆栈

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

从上图4可以看出,当初始状态为S4时,保存到mTempStateStack的节点为:
mTempStateStack={S4,S1,S0}
mTempStateStackCount = 3;
mStateStackTopIndex = -1;
然后调用函数moveTempStateStackToStateStack将节点以反序方式保存到mStateStack中

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

mStateStack={S0,S1,S4}
mStateStackTopIndex = 2
初始化完状态栈后,SmHandler将向消息循环中发送一个SM_INIT_CMD消息

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

该消息对应的处理如下:

else if (!mIsConstructionCompleted &&(mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
    mIsConstructionCompleted = true;
    invokeEnterMethods(0);
}
performTransitions();

消息处理过程首先调用invokeEnterMethods函数将mStateStack栈中的所有状态设置为激活状态,同时调用每一个状态的enter()函数

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

最后调用performTransitions函数来切换状态,同时设置mIsConstructionCompleted为true,表示状态机已经启动完成,SmHandler在以后的消息处理过程中就不在重新启动状态机了。

状态切换

SmHandler在处理每个消息时都会调用performTransitions来检查状态切换

private synchronized void performTransitions() {
  while (mDestState != null){
    //当前状态切换了 存在于mStateStack中的State需要改变
    //仍然按照链式父子关系来存储
    //先从当前状态S3找到 最近的被激活的parent状态S0
    //未被激活的全部保存起来(S3,S1) 返回S0
    StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
    //将mStateStack中 不属于当前状态(S3),
    //关系链上的State(S5,S2)退出(执行exit方法)
    invokeExitMethods(commonStateInfo);
    //将S3关系链 加入到栈中(S3,S1)
    int stateStackEnteringIndex = moveTempStateStackToStateStack();
    //将新加入到mStateStack中 未被激活的State激活(S3,S1)
    invokeEnterMethods(stateStackEnteringIndex);
    //将延迟的消息移动到消息队列的前面,以便快速得到处理               
    moveDeferredMessageAtFrontOfQueue();
  }
}

首先介绍一下状态切换的思路:
Android状态机源码分析_第5张图片
以上图中,初始状态为S4,现在目标状态mDestState被设置为S7。前面介绍了保存在mStateStack数组中的节点为:
mStateStack={S0,S1,S4}
mStateStackTopIndex = 2
这是以初始状态节点为起点遍历节点树得到的节点链表。
现在要切换到S7状态节点,则以S7为起始节点,同样遍历状态节点树,查找未激活的所有节点,并保存到mTempStateStack数组中
mTempStateStack={S7,S2,S0}
mTempStateStackCount = 3
接着调用mStateStack中除S0节点外的其他所有节点的exit函数,并且将每个状态节点设置为未激活状态,因此S4,S1被设置为未激活状态;将切换后的状态节点链表mTempStateStack移动到mStateStack,
mStateStack={S0,S2,S7}
mStateStackTopIndex = 2
并调用节点S2,S7的enter函数,同时设置为激活状态。
理解了整个状态切换过程后,就能更好地理解代码,首先根据目标状态建立状态节点链路表

            private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
            mTempStateStackCount = 0;
            StateInfo curStateInfo = mStateInfo.get(destState);
            do {
                mTempStateStack[mTempStateStackCount++] = curStateInfo;
                curStateInfo = curStateInfo.parentStateInfo;
            } while ((curStateInfo != null) && !curStateInfo.active);
            if (mDbg) {
                mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
                        + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
            }
            return curStateInfo;
        }

然后弹出mStateStack中保存的原始状态

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

将新建立的状态节点链表保存到mStateStack栈中

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

初始化入栈的所有新状态,并设置为激活状态

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

如何设置目标状态呢?StateMachine提供了transitionTo函数来切换状态

protected final void transitionTo(IState destState) {
    mSmHandler.transitionTo(destState);
}

该函数直接调用SmHandler的transitionTo函数来实现,SmHandler的transitionTo函数定义如下:

private final void transitionTo(IState destState) {
    mDestState = (State) destState;
    if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());
}

这里只是简单地设置了mDestState变量,并未真正更新状态栈mStateStack,在前面介绍了SmHandler在每次处理消息时都会自动更新一次mStateStack,无论mDestState变量值是否改变。由此可知目标状态的设置与状态栈的更新是异步的。

消息处理

StateMachine处理的核心就是SmHandler,就是一个Handler,运行在单独线程中。Handler是用来异步处理派发消息,这里使用Handler管理各个状态,派发消息处理到各个状态中去执行。StateMachine提供了多个消息发送接口,通过这些接口函数可以将消息发送到SmHandler中。

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

SmHandler将处理通过SmHandler发送的消息,处理过程如下:

public final void handleMessage(Message msg) {
    /** Save the current message */
    mMsg = msg;
    if (mIsConstructionCompleted) {
        //派发当前消息到state中去处理
        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);
    }
    //消息处理完毕更新mStateStack
    performTransitions();
}

变量mIsConstructionCompleted在状态机启动完成后被设置为true,因此这里将调用processMsg函数来完成消息处理。

private final void processMsg(Message msg) {
    StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
    //如果当前状态未处理该消息
    while (!curStateInfo.state.processMessage(msg)) {
        //将消息传给当前状态的父节点处理
        curStateInfo = curStateInfo.parentStateInfo;
        if (curStateInfo == null) {
             //当前状态无父几点,则丢弃该消息
            mSm.unhandledMessage(msg);
            if (isQuit(msg)) {
                transitionTo(mQuittingState);
            }
            break;
        }
    }
    //记录处理过的消息
    if (mSm.recordProcessedMessage(msg)) {
        if (curStateInfo != null) {
            State orgState = mStateStack[mStateStackTopIndex].state;
            mProcessedMessages.add(msg, mSm.getMessageInfo(msg), curStateInfo.state,orgState);
        } else {
            mProcessedMessages.add(msg, mSm.getMessageInfo(msg), null, null);
        }
    }
}

消息处理过程是从mStateStack栈顶派发到栈底,直到该消息被处理!

参考:http://blog.csdn.net/yangwen123/article/details/10591451#

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