概念
Android 中层次状态机的实现。
普通的状态机在状态机数量太多时不好维护,层次状态机会对状态机进行分类 ------ 也可以理解为按树型对状态机进行管理
。当有事件需要处理时,事件会由上至下一层层下发,结构更清晰。
StateMachine 由事件与状态两部分构成:状态以树的形式进行管理,事件会在树上进行分发。
使用
- 通过 addState() 添加不同状态,同时指定状态的父状态
- 使用 setInitialState() 设置状态机所处的初始状态
- 使用 start() 启动状态机
- 通过 sendMessage() 向状态机发送事件
- 每一个状态机通过 processMsg() 处理状态,当状态机进入/退出时,会调用 enter/exit
原理
属性
-
mStateInfo:存储所有状态的封装类 StateInfo。StateInfo 除了封装状态外,还记录状态的父状态,是否处于激活态
-
mTempStateStack:记录当前状态至根状态的路径。它是顺序存储:当前状态在第 0 号位,根状态在最后
-
mStateStack:与 mTempStateStack 作用类似,只不过它们顺序相反:根状态在第 0 号位,当前状态在最后。操作逻辑是先通过当前状态的 parent 属性往上遍历(遍历结果存储至 mTempStateStack 中,从下标为 0 开始存储),待遍历结束后再反向遍历 mTempStateStack 将结果存储至 mStateStack 中
start
StateMachine 内部所有操作逻辑都会转移至一个 HandlerThread 中。调用内部类(HandlerThread 的子类) 的 completeConstruction(),主要逻辑如下

setupInitialStateStack() 与 moveTempStateStackToStateStack() 的主要作用就是填充 mStateStack ------ 记录从根状态至当前状态的路径
sendMsg(SM_INIT_CMD) 会触发 invokeEnterMethods(),该方法才会执行真正的初始化逻辑:该方法就是遍历 mStateStack,然后调用每一个 State::enter() 方法
状态流转
StateMachine 的状态流转核心逻辑:当前 State 收到 msg 后,根据 msg 自己调用 transitionTo() 进行状态切换,而不是由统一状态管理类进行切换
。示例如下:
java
// 在 Msg 处理中调用 transitionTo() 进行状态切换
// 即由 State 根据 msg 自己作状态切换,而不是由统一的管理类进行切换
case(CMD_3):
deferMessage(message);
transitionTo(mP2);
retVal = HANDLED;
因此,状态切换其实就是向 StateMachine 发一个 msg,核心逻辑在 handleMessage() 中,代码分两步:
第一步:从当前 State 往上遍历,依次调用每一个 State::processMessage() 方法,直到某一个 State::processMessage() 返回 true,该 State 即为能处理 msg 的 State。如果需要进行状态切换,此 State 就需要调用 transitionTo() 进行处理。
第二步:状态切换,即将状态切换至 transitionTo 指定的目标状态,主要逻辑在 performTransitions() 中
- 根据 destState 填充 mTempStateStack: 存储 destState 至某一个祖先节点的路径。祖先节点是 destState 与 currentState 的最近的公共节点。StateMachine 以树结构管理所有 State,所以两个节点必然存在公共祖先节点,公共祖先节点及以上节点已经执行过 enter() 方法,在状态切换时不能再调用 enter()
- 对公共祖先节点以下的节点执行 exit() 方法,直到当前节点
- 从公共祖先节点(不含)至 destState 执行 enter() 方法
- 处理 deferMessage() 消息。在状态切换过程中除了调用 transitionTo() 进行状态切换外,还可以通过 sendMessage/deferMessage() 给目标状态传递消息
状态切换是由某个 msg 触发的,因此状态切换的所有逻辑都运行在该 msg 对应的 handleMessage() 中。Message 又是通过 queue 进行管理,所以状态切换完后 queue 中的 message 都由新状态进行处理。
而 deferMessage() 会将 Msg 插入到 queue 队头,新状态会先处理 deferMessage() 发送的消息,然后再处理 sendMessage() 发送的消息
。此即 deferMessage() 与 sendMessage() 的区别。