状态模式:当一个对象的内在状态改变时,允许改变其行为。状态模式中行为是由状态来决定的,不同的状态下有不同的行为。状态模式的意图是让一个对象在其内部状态改变时,其行为也随之改变。
状态模式的UML类图:
Context:环境类,定义客户感兴趣的接口,维护一个State子类的实例,这个实例定义了对象的当前状态。
State:抽象状态类或者状态接口,定义一个或者一组接口,表示该状态下的行为。
ConcreteStateA、ConcreteStateB:具体状态类,每一个具体的状态类实现抽象State中定义的接口,从而达到不同状态下的不同行为。
接下来我们将分析Android中的StateMachine实现。首先看下StateMachine的UML类图,从图中可以看到,StateMachine的UML类图符合状态模式的设计原则,IState接口代表一组接口,表示该状态下的行为。HaltingState和QuittingState代表具体的状态,StateMachine对象维护当前状态,提供客户端感兴趣的接口。
IState接口定义在frameworks/core/java/com/android/internal/util/IState.java中
State接口定义frameworks/core/java/com/android/internal/util/State.java类中
StateMachine类定义在frameworks/core/java/com/android/internal/util/StateMachine.java中。
IState接口
IState接口定义了一组方法,表示该状态下的行为。
- enter()
- exit()
- processMessage()
- getName()
State类
State类提供一个IState接口的基本实现。State类必须实现processMessage方法,可选地实现enter和exit函数,这两个方法等价于构造函数和析构函数,用于初始化和清理相关的工作。
StateMachine类
StateMachine是一个层次式状态机,可以管理和处理状态的跳转。StateMachine是基于Handler来实现的,通过Message来驱动状态的跳转。首先来看下状态机的构建:
/**
* 状态机的构建函数
*
* @param name 状态机的名字
*/
protected StateMachine(String name) {
//通过HandlerThread来处理消息
mSmThread = new HandlerThread(name);
mSmThread.start();
Looper looper = mSmThread.getLooper();
initStateMachine(name, looper);
}
/**
* 初始化状态机
*
* @param looper 状态机使用的Looper
* @param name 状态机名字
*/
private void initStateMachine(String name, Looper looper) {
mName = name;
mSmHandler = new SmHandler(looper, this);
}
/*
* SmHandler继承自Handler
*/
private SmHandler(Looper looper, StateMachine sm) {
super(looper);
mSm = sm;//持有一个状态机的引用
//将Halting和Quitting状态添加到状态列表中
addState(mHaltingState, null);
addState(mQuittingState, null);
}
当一个状态机被创建,通过addState函数来构建状态的层级,通过setInitialState函数来设置初始状态。
/**
* 添加一个新的状态到状态机
*
*
* @param 需要添加的状态
* @param parent the 状态的父状态
* @return stateInfo 返回当前状态的信息
*/
private final StateInfo addState(State state, State parent) {
StateInfo parentStateInfo = null;
if (parent != null) {
parentStateInfo = mStateInfo.get(parent);
if (parentStateInfo == null) {
//如果parent状态还未添加,则先添加parent状态
parentStateInfo = addState(parent, null);
}
}
StateInfo stateInfo = mStateInfo.get(state);
if (stateInfo == null) {
stateInfo = new StateInfo();
//将状态添加到状态集合中
mStateInfo.put(state, stateInfo);
}
//验证是否存在相同的状态,不同parent的情况,保证一个状态只有一个parent状态存在
if ((stateInfo.parentStateInfo != null)
&& (stateInfo.parentStateInfo != parentStateInfo)) {
throw new RuntimeException("state already added");
}
stateInfo.state = state;
stateInfo.parentStateInfo = parentStateInfo;
stateInfo.active = false;
return stateInfo;
}
/*
* 状态信息
*/
private class StateInfo {
/** 状态 */
State state;
/** 父类状态信息,如果没有父类状态,则为空 */
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());
}
}
//保存状态机中所有的状态
private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();
当把状态添加到状态机列表后,需要通过setInitialState函数设置状态机的初始状态。
/*
* 设置状态机的初始化状态
*/
public final void setInitialState(State initialState) {
mSmHandler.setInitialState(initialState);
}
private final void setInitialState(State initialState) {
if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName());
mInitialState = initialState;
}
//状态机的初始状态,将处理第一个消息
private State mInitialState;
设置完状态机的初始状态之后,可以调用start方法,启动状态机。
/*
* 启动状态机
*/
public void start() {
SmHandler smh = mSmHandler;
if (smh == null) return;
smh.completeConstruction();
}
/*
* 完成状态机的构建
*/
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;
}
}
//根据状态树最大高度穿件StateStack数组和TempStateStack数组
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));
}
/*
* 初始化StateStack数组
*/
private final void setupInitialStateStack() {
//获取初始状态的状态信息
StateInfo curStateInfo = mStateInfo.get(mInitialState);
//将初始状态以及初始状态的父状态和祖先状态放入TempStateStack数组中
for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
mTempStateStack[mTempStateStackCount] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
}
// StateStack的索引,初始化时为-1,表示是一个空数组
mStateStackTopIndex = -1;
moveTempStateStackToStateStack();
}
/*
* 将TempStackState数组中的状态转移到StateStack数组中,转移的顺序是反序的。
*/
private final int moveTempStateStackToStateStack() {
//StateStack数组的起始索引,这里为0
int startingIndex = mStateStackTopIndex + 1;
//TempStateStack数组最后一个元素的索引
int i = mTempStateStackCount - 1;
int j = startingIndex;
//循环将TempStateStack数组中的元素以倒序的方式存入StateStack数组中
while (i >= 0) {
mStateStack[j] = mTempStateStack[i];
j += 1;
i -= 1;
}
//StateStack数组的索引指向最后一个元素
mStateStackTopIndex = j - 1;
return startingIndex;
}
举个例子来说,状态树如下:
mP0
/ \
mP1 mS0
/ \
mS2 mS1
/ \ \
mS3 mS4 mS5 ---> initial state
状态树的最大高度maxDepth为3,当初始状态为mS5时,将初始状态及其父状态和祖先状态存入TempStateStack数组中,即mS5,mS1,mP1,mP0。接着将TempStackState数组中的元素以倒序的方式存入StateStack数组中,即mP0,mP1,mS1,mS5。mStateStackTopIndex索引指向数组的最后一个元素,即mStateStackTopIndex为3。
需要说明的是,上面所说的状态树并不是真正的树数据结构,因为没有树的根节点,可以理解为有交叉的单链表结构。
当完成了TempStackState和StateStack数组的初始化之后,发送了一个SM_INIT_CMD消息到消息队列的首部,触发状态的启动。
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
sendMessageAtFrontOfQueue发送一个消息到消息队列的首部,让该消息最先处理。
/*
* 通过调用当前状态的processMessage方法处理消息
* 调用状态的enter/exit方法
* 将deferred的消息放入到消息队列的首部,在新的状态中处理
*
*/
@Override
public final void handleMessage(Message msg) {
//状态机还未停止
if (!mHasQuit) {
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
//可以覆写onPreHandleMessage方法,在消息处理前做一些工作
mSm.onPreHandleMessage(msg);
}
//保存当前消息
mMsg = msg;
//保存处理当前信息的状态
State msgProcessedState = null;
if (mIsConstructionCompleted) {
// 1.调用processMessage处理消息
msgProcessedState = processMsg(msg);
} else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
&& (mMsg.obj == mSmHandlerObj)) {
//初始化第一次时调用到这里
mIsConstructionCompleted = true;
invokeEnterMethods(0);
} else {
throw new RuntimeException("StateMachine.handleMessage: "
+ "The start method not called, received msg: " + msg);
}
// 2.调用状态的enter/exit方法,将deferred的消息放入消息队列的首部
performTransitions(msgProcessedState, msg);
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
//可以覆写onPostHandleMessage方法,在消息处理完成后做一些工作
mSm.onPostHandleMessage(msg);
}
}
}
当收到SM_INIT_CMD消息,并且Message的obj为mSmHandlerObj时,表示这是第一次初始化状态。
/*
* 触发StateStack数组中元素进入enter状态,从stateStackEnteringIndex到mStateStackTopIndex之间的状态都将进入enter状态,并将active设置为true。
*/
private final void invokeEnterMethods(int stateStackEnteringIndex) {
for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
if (stateStackEnteringIndex == mStateStackTopIndex) {
mTransitionInProgress = false;
}
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
mTransitionInProgress = false;
}
当发送完SM_INIT_CMD消息之后,所有初始化相关的状态都执行enter方法,进入等待处理消息。StateMachine类是基于HandlerThread实现的,当状态机被创建并启动后,消息是通过sendMessage方法发送到当前状态处理。当前状态调用processMessage()方法来处理该消息。如果当前状态没有处理完该消息,则交由它的父状态的processMessage处理。
public void sendMessage(int what) {
SmHandler smh = mSmHandler;
if (smh == null) return;
smh.sendMessage(obtainMessage(what));
}
当接收到新的消息后,StateMachine会调用handleMessage来处理该消息,并调用当前状态的processMessage方法来处理消息。状态机出初始化完成后,将调用processMsg方法来处理消息。
在状态机中的,每个状态都有0个或者一个父状态。如果子状态不能处理完消息,则交由父类状态继续处理,或者它的祖先处理。如果父状态或其祖先还未能处理完成消息,则调用unhandleMessage方法做最后的处理。
/*
* 处理该消息,如果当前状态不能处理该消息,则交由其父状态处理以及祖先处理。
* 如果该消息最终还未被处理,则交由状态机的unhandleMessage方法来处理。
*/
private final State processMsg(Message msg) {
//获取当前状态信息
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
if (isQuit(msg)) {
//如果是终止消息,则转移到终止状态
transitionTo(mQuittingState);
} else {
while (!curStateInfo.state.processMessage(msg)) {
获取当前状态的父状态信息
curStateInfo = curStateInfo.parentStateInfo;
if (curStateInfo == null) {
/**
* No parents left so it's not handled
*/
mSm.unhandledMessage(msg);
break;
}
}
}
//返回处理该消息的状态
return (curStateInfo != null) ? curStateInfo.state : null;
}
processMessage方法返回处理消息的状态,接下来处理状态转移操作。状态转移的大致流程如下:
1.获取当前状态与目标状态的共同祖先;
2.将从当前状态到共同祖先状态(不包含共同祖先)之间的所有状态都执行exit方法;
3.将从共同祖先状态到目标状态之间的所有状态都执行enter方法;
4.判断是否是quit或halt状态,并执行相应的操作;
当一个状态跳转到其他状态时,会导致当前状态被退出,进入新的状态。为了找出哪些状态需要执行exit退出操作,哪些状态需要执行enter进入操作,首先需要找到当前状态与新状态的最近公共祖先状态。然后我们从当前状态到公共祖先状态之间的状态(但不包含公共祖先状态),都执行exit退出操作。从公共祖先状态到新状态之间的状态都执行enter进入操作。如果当前状态与新状态没有公共祖先状态,则当前状态及其父状态和祖先状态都执行exit操作,并执行新状态的enter操作。
/*
* 状态转移操作
*/
private void performTransitions(State msgProcessedState, Message msg) {
.........
//目标状态,通过transitionTo方法设置目标状态
State destState = mDestState;
if (destState != null) {
while (true) {
//获取当前状态和目标状态共同的祖先状态
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
mTransitionInProgress = true;
//从当前状态到公共祖先状态之间的状态都执行exit方法
invokeExitMethods(commonStateInfo);
//获取进入enter状态的索引
int stateStackEnteringIndex = moveTempStateStackToStateStack();
//从公共祖先状态到目标状态之间的状态都执行enter方法
invokeEnterMethods(stateStackEnteringIndex);
//将deferred的消息放入到消息队列的首部,在新的状态中处理
moveDeferredMessageAtFrontOfQueue();
if (destState != mDestState) {
// A new mDestState so continue looping
destState = mDestState;
} else {
// No change in mDestState so we're done
break;
}
}
mDestState = null;
}
//如果目标状态是quit状态,则执行onQuitting操作,如果是halt状态,则执行onHalting操作
if (destState != null) {
if (destState == mQuittingState) {
mSm.onQuitting();
cleanupAfterQuitting();
} else if (destState == mHaltingState) {
mSm.onHalting();
}
}
}
/*
* 将需要执行enter操作的状态保存到TempStateStack数组中
* 返回当前状态与目标状态的共同祖先状态,如果没有则返回目标状态信息
*/
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);
return curStateInfo;
}
/*
* 将当前状态到共同祖先状态之间的状态执行exit方法
*/
private final void invokeExitMethods(StateInfo commonStateInfo) {
//从StateStack数组尾部索引开始查找,直到公共祖先状态,但不包含公共祖先状态,都执行exit方法
while ((mStateStackTopIndex >= 0)
&& (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
State curState = mStateStack[mStateStackTopIndex].state;
curState.exit();
mStateStack[mStateStackTopIndex].active = false;
mStateStackTopIndex -= 1;
}
}
/*
* 将TempStackState数组中的状态转移到StateStack数组中,转移的顺序是反序的。
*/
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;
}
/*
* 从stateStackEnteringIndex索引开始,将StateStack数组中的状态执行enter操作
*/
private final void invokeEnterMethods(int stateStackEnteringIndex) {
for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
if (stateStackEnteringIndex == mStateStackTopIndex) {
mTransitionInProgress = false;
}
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
mTransitionInProgress = false;
}
/*
* 将deferred的消息放入到消息队列的首部
*/
private final void moveDeferredMessageAtFrontOfQueue() {
//将deferred数组中消息放入消息队列的首部,最早放入的数组的消息,在消息队列的最前面
for (int i = mDeferredMessages.size() - 1; i >= 0; i--) {
Message curMsg = mDeferredMessages.get(i);
sendMessageAtFrontOfQueue(curMsg);
}
mDeferredMessages.clear();
}
还是以上面的例子来说, 现在假设mS5的processMessage能处理当前消息,并且在处理过程中,转移状态到mS4(transitionTo(mS4))。当从processMessage返回时,将会去查找mS5和mS4的公共祖先——mP1。所以接着执行mS5.exit,mS1.exit;mS2.enter,mS4.enter。新的活动列表是mP0,mP1,mS2,mS4。当下一个消息到达时,最先执行的是mS4.processMessage方法。
在状态处理消息的时候,可以通过transitionTo方法跳转到其他的状态。
/*
* 转移到目标状态
*/
public final void transitionTo(IState destState) {
mSmHandler.transitionTo(destState);
}
private final void transitionTo(IState destState) {
mDestState = (State) destState;
}
当所有的状态都处理完成后,则状态机会选择调用transitionToHaltingState()方法。
/*
* 转移到终止状态
*/
public final void transitionToHaltingState() {
mSmHandler.transitionTo(mSmHandler.mHaltingState);
}
如果想完全终止状态机,可以调用quit方法或者quitNow方法。这些方法将会调用exit方法退出当前状态和其父类状态,然后调用onQuiting,退出线程和Looper。
public final void quit() {
SmHandler smh = mSmHandler;
if (smh == null) return;
smh.quit();
}
private final void quit() {
sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
}
一个具体的例子:
在构建StateMachine对象的时候,将所有的状态都添加到状态列表中,并将初始状态设置为mS1。状态树为
mP1 mP2
/ \
mS2 mS1 ---->初始状态
然后调用start方法启动状态机,执行初始状态mS1以及mP1的enter方法,进入等待处理消息状态。
当状态机通过sendMessage方法发送消息时,当前状态会开始处理消息,在消息处理的过程中,可以通过transitionTo转移到其他的状态。
public class StateMachineTest extends StateMachine {
private static final String TAG = "StateMachineTest";
public static final int CMD_1 = 1;
public static final int CMD_2 = 2;
public static final int CMD_3 = 3;
public static final int CMD_4 = 4;
public static final int CMD_5 = 5;
public static StateMachineTest makeStateMachine(){
Log.e(TAG, "makeStateMachine()...");
StateMachineTest machine = new StateMachineTest("StateMachineTest");
machine.start();
return machine;
}
protected StateMachineTest(String name) {
super(name);
Log.e(TAG, "StateMachineTest()...");
addState(mP1);
addState(mS1, mP1);
addState(mS2, mP1);
addState(mP2);
setInitialState(mS1);
}
class P1 extends State{
@Override
public void enter() {
Log.e(TAG, "P1 enter()");
}
@Override
public boolean processMessage(Message message) {
Log.e(TAG,"P1 processMessage()...message.what = " + message.what);
boolean retVal;
switch (message.what){
case CMD_2:
sendMessage(obtainMessage(CMD_3));
deferMessage(message);
transitionTo(mS2);
retVal = HANDLED;
break;
default:
retVal = NOT_HANDLED;
break;
}
return retVal;
}
@Override
public void exit() {
Log.e(TAG, "P1 exit()");
}
}
class P2 extends State{
@Override
public void enter() {
Log.e(TAG, "P2 enter()");
sendMessage(obtainMessage(CMD_5));
}
@Override
public boolean processMessage(Message message) {
Log.e(TAG,"P2 processMessage()...message.what = " + message.what);
switch (message.what){
case CMD_3:
break;
case CMD_4:
break;
case CMD_5:
transitionToHaltingState();
break;
}
return HANDLED;
}
@Override
public void exit() {
Log.e(TAG, "P2 exit()");
}
}
class S1 extends State{
@Override
public void enter() {
Log.e(TAG, "S1 enter()");
}
@Override
public boolean processMessage(Message message) {
Log.e(TAG,"S1 processMessage()...message.what = " + message.what);
boolean retVal;
switch (message.what){
case CMD_1:
transitionTo(mS1);
retVal = HANDLED;
break;
default:
retVal = NOT_HANDLED;
break;
}
return retVal;
}
@Override
public void exit() {
Log.e(TAG, "S1 exit()");
}
}
class S2 extends State{
@Override
public void enter() {
Log.e(TAG, "S2 enter()");
}
@Override
public boolean processMessage(Message message) {
Log.e(TAG,"S2 processMessage()...message.what = " + message.what);
boolean retVal;
switch (message.what){
case CMD_2:
sendMessage(obtainMessage(CMD_4));
retVal = HANDLED;
break;
case CMD_3:
deferMessage(message);
transitionTo(mP2);
retVal = HANDLED;
break;
default:
retVal = NOT_HANDLED;
break;
}
return retVal;
}
@Override
public void exit() {
Log.e(TAG, "S2 exit()");
}
}
@Override
protected void onHalting() {
Log.e(TAG, "onHalting()....");
synchronized (this){
this.notifyAll();
}
}
P1 mP1 = new P1();
P2 mP2 = new P2();
S1 mS1 = new S1();
S2 mS2 = new S2();
}
测试方法:
void testStateMachine(){
StateMachineTest sm = StateMachineTest.makeStateMachine();
synchronized (sm){
sm.sendMessage(Message.obtain(sm.getHandler(),StateMachineTest.CMD_1));
sm.sendMessage(Message.obtain(sm.getHandler(), StateMachineTest.CMD_2));
try {
sm.wait();
}catch (InterruptedException e){
Log.e(TAG,"exception while waiting " + e.getMessage());
}
}
}
代码执行结果为:
05-25 14:19:39.603 22337-22337/com.example.statemachinedemo E/StateMachineTest﹕ makeStateMachine()...
05-25 14:19:39.645 22337-22337/com.example.statemachinedemo E/StateMachineTest﹕ StateMachineTest()...
05-25 14:19:39.646 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ P1 enter()
05-25 14:19:39.646 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ S1 enter()
05-25 14:19:39.647 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ S1 processMessage()...message.what = 1
05-25 14:19:39.648 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ S1 exit()
05-25 14:19:39.648 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ S1 enter()
05-25 14:19:39.649 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ S1 processMessage()...message.what = 2
05-25 14:19:39.649 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ P1 processMessage()...message.what = 2
05-25 14:19:39.649 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ S1 exit()
05-25 14:19:39.650 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ S2 enter()
05-25 14:19:39.650 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ S2 processMessage()...message.what = 2
05-25 14:19:39.650 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ S2 processMessage()...message.what = 3
05-25 14:19:39.650 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ S2 exit()
05-25 14:19:39.651 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ P1 exit()
05-25 14:19:39.651 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ P2 enter()
05-25 14:19:39.651 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ P2 processMessage()...message.what = 3
05-25 14:19:39.651 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ P2 processMessage()...message.what = 4
05-25 14:19:39.652 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ P2 processMessage()...message.what = 5
05-25 14:19:39.652 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ P2 exit()
05-25 14:19:39.652 22337-22373/com.example.statemachinedemo E/StateMachineTest﹕ onHalting()....
完整代码例子在 https://github.com/qiubing/StateMachineDemo