State模式允许一个对象在期内部状态改变时改变其行为模式,State 模式是将对象的状态封装成一个对象,是在不同的状态下执行不同的操作。Android系统通过StateMachine 是State模式的一种应用,其实现的相当精妙。本文对StateMachine做个深入分析,介绍其实现机制。
首先通过一个例子来引入问题,假如我们有6个状态S1、S2、S3、S4、S5、S6。其中:1、每个状态对某些消息都要做不同的处理;2、S1是S2的父状态,S2是S3的父状态,S3是S4的父,S1是S5的父状态,S5是S6的父状态;3、初始状态是S4;4、在运行过程中,收到某个消息时状态机有S4转到S3。如下图:
图1:状态结构图
图1显示了个状态的关系,在解析Android StateMachine如何管理State之前,先介绍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);
}
}
State 作为状态基类,StateMachine 管理的state都需要从此继承下来。重写几个重要方法,enter表示进入该状态的调用,exit是退出该状态会调用,processMessage是当前状态下处理消息的函数。如果当前状态处理后返回false表明还需要父状态处理,如图1中示例,如果状态S4处理消息后返回false表明还需要其父状态S2处理,直到递归到根状态。通过这样的机制为设计状态机的各种状态提供了便利,对于一些公共消息可以放到公共状态上来处理,后面会介绍其实现的机制。
在Android Handler机制博文中介绍了Android线程处理消息的一种方式,在StateMachine中,核心也是采用了类似的机制来管理states。StateMachine通过一独立线程来完成三个功能
- 建立树型状态层次结构
- 状态机状态堆栈的建立并管理状态切换
- 分发消息
以上三个功能均有mSmHandler来完成,mSmHandler类继承于Handler,下面来分析其如何完成以上三个功能。
1. 建立树形结构层次
StateMachine 提供AddState方法来添加状态到状态机中,函数如下:
protected final void addState(State state, State parent) {
mSmHandler.addState(state, parent);
}
这里其实调用的是mSmHandler的addState 方法,再网下跟踪
private final StateInfo addState(State state, State parent) {
// 获取当前状态父状态信息
StateInfo parentStateInfo = null;
if (parent != null) {
parentStateInfo = mStateInfo.get(parent);
if (parentStateInfo == null) {
// 当父状态未添加进来时,递归添加进来.
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;
}
在StateMachine类中,StateInfo就是一个包装State的树节点,每一个状态对应一个状态信息StateInfo,通过HashMaps存储下来
private HashMap<State, StateInfo> mStateInfo =new HashMap<State, StateInfo>();
因此图1所的示状态构成可以通过以下流程来实现:
addState(S1,null);addState(S2,S1);addState(S3,S2);addState(S4,S2);addState(S5,S1);addState(S6,S5);
2.1 状态机堆栈建立
StateMachine 通过两个结构来建立和切换状态堆栈, mStateStack[] 及 mTempStateStack[]。建立堆栈API如下:
private final void completeConstruction() {
// 计算堆栈深度,状态个数
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();
// 激活对应状态,并调用State的entry方法表明进入当前状态
mIsConstructionCompleted = true;
mMsg = obtainMessage(SM_INIT_CMD);
invokeEnterMethods(0);
//如果需要将做状态切换,在后面会介绍
performTransitions();
}
在来分析setUpInitialStateStack()的实现。
private final void setupInitialStateStack() {
// 根据初始状态建立临时栈,如本文开始提出的初始状态为S4,那么临时栈存储的状态为:S4->S2->S1
StateInfo curStateInfo = mStateInfo.get(mInitialState);
for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
mTempStateStack[mTempStateStackCount] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
}
// Empty the StateStack
mStateStackTopIndex = -1;
// 将临时栈数据反过来存到state 栈中,即S1->S2->S4 来保持堆栈先进后出的机制
moveTempStateStackToStateStack();
}
2.2 状态切换
SateMachine 提供的方法是:
protected final void transitionTo(IState destState) {
mSmHandler.transitionTo(destState);
}
mSmHandler 提供的方法是:
private final void transitionTo(IState destState) {
mDestState = (State) destState;
}
从中可以看出mSmHandler只是记录了一下切换到的目的状态,真正的切换实现在performTransitions 中。
private void performTransitions() {
State destState = null;
while (mDestState != null) {
destState = mDestState;
mDestState = null;
//当前状态切换了存在于mStateStack中的State需要改变
//仍然按照链式父子关系来存储
//先从当前状态S4找到 最近的被激活的parent状态S4
//未被激活的全部保存起来(S3) 返回S3
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
invokeExitMethods(commonStateInfo);
// 将临时栈的state修改到State栈中
int stateStackEnteringIndex = moveTempStateStackToStateStack();
invokeEnterMethods(stateStackEnteringIndex);
}
}
mTempStateStack 原来的数据是: S4-S2-S1;mStateStack状态切换前是:S1-S2-S4 栈顶是2状态切换到S3后,mTempStateStack的数据是:S3-S2-S1;mStateStack状态是S1-S2-S3。
3. 消息分发
mSmHandler实际是一Handler对象,消息处理函数在handleMessage方法中。
public final void handleMessage(Message msg)
{
//处理当前消息到state中去处理
processMsg(msg);
//消息处理完毕状态切换 更新mStateStack
performTransitions();
}
private final void processMsg(Message msg)
{
//派发消息到state中去处理
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
while (!curStateInfo.state.processMessage(msg))
{
//当前状态mDestState 未处理该消息,交给其parent state处理
curStateInfo = curStateInfo.parentStateInfo;
if (curStateInfo == null){
//此消息未被当前层次状态链处理
}
}
}
SateMachine 通过堆栈来管理State一是方便状态切换,二是实现消息的链式处理,功能强大。除此之外,SateMachine还提供处理完消息记录功能及延时消息处理功能,
在这里就不再介绍了。参考方法deferMessage,ProcessedMessages的实现。