概念

Android状态机是一种分层安排状态来处理消息的分层状态机制。

也就是说状态机机制就是拥有多个不同状态state,并能够根据不同的msg来切换不同状态进行处理的机制

从这个概念性描述可以看出一个状态机机制中会含有多个state状态,在状态机中会存在msg的分发,也会有对state切换的管理,每个state状态中会有对应msg的处理。

既然是要分发和处理msg,那么对应着就会需要一个handler对象。真正对msg做处理的是state对象。

使用

总共分以下几步:

  1. 创建所需要的State状态类

  2. 创建继承自StateMchine的状态机管理

  3. 启动/关闭状态机

创建所需要的状态机State

创建state状态类P1

 1state mP1 {
2     enter { log("mP1.enter"); }
3     exit { log("mP1.exit");  }
4     on msg {
5         CMD_2 {
6             send(CMD_3);
7             defer(msg);
8             transitionTo(mS2);
9             return HANDLED;
10         }
11         return NOT_HANDLED;
12     }
13}

 

在p1中重写了processMessage方法

当接收到msg.what为CMD_2时会做如下操作

  • 第一,send发送CMD_3的命令

  • 第二,defer延迟当前的CMD_2的msg,下一个state状态时会触发该msg

  • 第三,transitionTo切换到下一个状态mS2

  • 第四,除CMD_2以外的其他消息均返回NOT_HANDLED交由父状态处理

创建state状态类P2

 1state mP2 {
2     enter {
3         log("mP2.enter");
4         send(CMD_5);
5     }
6     exit { log("mP2.exit"); }
7     on msg {
8         CMD_3, CMD_4 { return HANDLED; }
9         CMD_5 {
10             transitionTo(HaltingState);
11             return HANDLED;
12         }
13         return NOT_HANDLED;
14     }
15}

 

P2会做如下处理

  • 第一,enter方法中即在进入该state时会send发送CMD_5的msg

  • 第二,在接收到CMD_3/CMD_4直接返回HANDLED表示已经进行了处理

  • 第三,在接收到CMD_5时transitionTo切换到HaltingState状态

  • 第四,如果接收到其他消息,则不做任何处理,交由父类处理

创建state状态类S1,继承自P1

 1state mS1 parent mP1 {
2     enter { log("mS1.enter"); }
3     exit  { log("mS1.exit");  }
4     on msg {
5         CMD_1 {
6             transitionTo(mS1);
7             return HANDLED;
8         }
9         return NOT_HANDLED;
10     }
11}

 

  • 第一,接收到CMD_1时切换到mS1,自切换状态,这个做法也会导致自身exit-->enter

  • 第二:接收到其他消息时不做任何处理,交由父类处理

创建state状态类S2:继承自P1

 1state mS2 parent mP1 {
2     enter { log("mS2.enter"); }
3     exit  { log("mS2.exit");  }
4     on msg {
5         CMD_2 {
6             send(CMD_4);
7             return HANDLED;
8         }
9         CMD_3 {
10             defer(msg);
11             transitionTo(mP2);
12             return HANDLED;
13         }
14         return NOT_HANDLED;
15     }
16}

 

  • 第一,接收到CMD_2时发送CMD_4的消息

  • 第二,接收到CMD_3时先defer当前msg,再切换到P2

  • 第三,其他消息不做任何处理,交由父类处理

statemachine状态类

 1class Hsm1 extends StateMachine {
2    public static final int CMD_1 = 1;
3    public static final int CMD_2 = 2;
4    public static final int CMD_3 = 3;
5    public static final int CMD_4 = 4;
6    public static final int CMD_5 = 5;
7
8    public static Hsm1 makeHsm1() {
9        log("makeHsm1 E");
10        Hsm1 sm = new Hsm1("hsm1");
11        sm.start();
12        log("makeHsm1 X");
13        return sm;
14    }
15
16    Hsm1(String name) {
17        super(name);
18        log("ctor E");
19
20        // Add states, use indentation to show hierarchy
21        addState(mP1);
22            addState(mS1, mP1);
23            addState(mS2, mP1);
24        addState(mP2);
25
26        // Set the initial state
27        setInitialState(mS1);
28        log("ctor X");
29    }
30......
31    void onHalting() {
32        log("halting");
33        synchronized (this) {
34            this.notifyAll();
35        }
36    }
37}

 

  • addState:将状态添加到状态机

  • setInitialState:初始化状态机的初始化状态为mS1

执行状态机

 1Hsm1 hsm = makeHsm1();
2synchronize(hsm) {
3     hsm.sendMessage(obtainMessage(hsm.CMD_1));
4     hsm.sendMessage(obtainMessage(hsm.CMD_2));
5     try {
6          // wait for the messages to be handled
7          hsm.wait();
8     } catch (InterruptedException e) {
9          loge("exception while waiting " + e.getMessage());
10     }
11}

 

  • 第一:makeHsm1:调用start方法开启状态机

  • 第二:sendMessage:发送消息让状态机开始处理

最终结果为

 1D/hsm1    ( 1999): makeHsm1 E
2D/hsm1    ( 1999): ctor E
3D/hsm1    ( 1999): ctor X
4D/hsm1    ( 1999): mP1.enter
5D/hsm1    ( 1999): mS1.enter
6D/hsm1    ( 1999): makeHsm1 X
7D/hsm1    ( 1999): mS1.processMessage what=1
8D/hsm1    ( 1999): mS1.exit
9D/hsm1    ( 1999): mS1.enter
10D/hsm1    ( 1999): mS1.processMessage what=2
11D/hsm1    ( 1999): mP1.processMessage what=2
12D/hsm1    ( 1999): mS1.exit
13D/hsm1    ( 1999): mS2.enter
14D/hsm1    ( 1999): mS2.processMessage what=2
15D/hsm1    ( 1999): mS2.processMessage what=3
16D/hsm1    ( 1999): mS2.exit
17D/hsm1    ( 1999): mP1.exit
18D/hsm1    ( 1999): mP2.enter
19D/hsm1    ( 1999): mP2.processMessage what=3
20D/hsm1    ( 1999): mP2.processMessage what=4
21D/hsm1    ( 1999): mP2.processMessage what=5
22D/hsm1    ( 1999): mP2.exit
23D/hsm1    ( 1999): halting

 

状态机继承树如下

Android状态机StateMachine研究_java

 

执行过程如下:

  • start开启状态机后会进入到初始化的state的enter方法(先触发父类enter),mP1.enter-->mS1.enter

  • 当状态机sendMessage:CMD_1时,此时当前处于激活态的state为S1,所以会交由S1的processMessage方法来处理,mS1.processMessage what=1.

  • 在S1接收到CMD_1的消息之后又transitionTo进行了一个自切换,所以会首先退出本次State再开启下一次State。在退出时会依次调用子类-->父类的exit.但如果要切换的state为父State的子State(继承自State的除外),则只会调用子State的exit,之后再激活要进入的state,mS1.exit-->mS1.enter。到此,没有任何退出操作或者是状态切换操作,所以保持在S1状态

  • 此时状态机sendMessage:CMD_2,当前激活状态为S1,所以会交由S1处理,但因为S1返回了not_handled,所以交由S1的父类P1来处理,mS1.processMessage what = 2--->mP1.processMessage what = 2

  • 接下来看P1的处理,P1发送了CMD_3的消息,并且将CMD_2的消息推迟到了下一个状态,之后切换到S2,既然切换到下一个状态,那么就会退出当前S1和他的父类P1,当因为S2是P1的子类,所以不会退出P1,所以是mS1.exit--->mS2.enter

  • 此时状态机激活态为S2,所以由S2来处理CMD_2和CMD3,mS2.processMessage what = 2--->mS2.processMessge what = 3

  • 当S2收到CMD_2时,会发送CMD_4。当S2接收到CMD_3时,会发送一个CMD_3给下一个要激活的状态,并切换到下一个要激活的P2,在发生状态切换时就会结束上一个状态,并开启下一个状态,所以,mS2.exit-->mP1.exit-->mP2.enter

  • 在出发P2.enter时,会send一个CMD_5给当前激活态。到目前为止,消息队列里已经有CMD_4,CMD_3,CMD_5,又因为deferMessage是将消息插入到消息队列最开始,所以P2处理顺序如下,mP2.processMessage what = 3--->mP2.processMessage what = 4-->mP2.processMessage what = 5

  • 在P2接收到CMD_5时会切换到一个状态机暂停的状态HaltingState(来自父类StateMachine),发生了状态切换,所以P2退出,P2.exit

 

原理

源码中状态机重点有四个类,

State: 继承自IState,状态机状态描述对象,每个对象会有四个行为

  1. enter:切换到该状态时会触发该方法

  2. exit:当退出该状态时触发

  3. boolean processMessage:(必须要实现)针对msg的处理,如果返回false,则会交由parentstate父状态来处理

  4. String getName:获取对于该state的一个name描述

StateInfo: 用于存储状态机信息,是StateMachine的内部类,那么这个info需要包含哪些信息呢?既然状态机包含很多状态,那么一定需要标识出目前处于哪个state.对应的来说,

state也需要一个字段来表示当前state是否是处于激活的状态

  1. active boolean值,当前state是否处于激活状态

  2. parentstate: state的父类

  3. State: 和stateInfo相对应的state

SmHandler: 继承自handler,是statemachine的内部类,用于分发和处理msg的handler,

handler就两个功能,一个是分发msg,一个是处理msg

  1. sendMessage,发送msg,handler相关的发送msg的一些方法

  2. deferMessage:该方法就是将msg推迟到下一个状态切换,该msg不会被立即发送,而是在切换到下一个state后handler才会发送该msg,让该state来处理。所以SmHandler中会有一个用于存储defer传送过来的msg的数据结构

  3. handleMessage:重写父类handler的处理msg的方法,在该方法中会将接收到的消息交个当前的state来处理

  4. initial/add/remove状态state:初始化/添加或者删除状态

  5. state状态保存/切换/启动/退出机制

Statemachine:状态机类,用于管理以上消息和state,会将SmHandler的方法进行一个包装提供给其他类使用.

这个封装实现其实就是实现两个功能

  1. 消息队列的管理

  2. state的管理

消息队列的管理

因为实现了defer,所以有对消息队列的顺序做出一些调整,SmHandler有一个用于存储被延迟的消息的list列表mDeferredMessages

1/** The list of deferred messages */
2private ArrayList<Message> mDeferredMessages = 
             new ArrayList<Message>();

 

SmHandler还需要提供基本的发送消息send方法和移除消息remove方法。

当有消息到来时会触发SmHandler的handleMessage方法,进而触发各个state的processMessage方法

state状态的管理

包括当前处于激活的是什么状态state,该State的继承树是怎样的,也就是说他的父state以及父State的state。还有要切换到的state的继承树是怎样的。

只有清楚了这两个结构,才能在方便调用exit和enter,在调用enter时会将该继承树中的所有state的active设置true,在调用exit时会将该继承树中的所有state的active设置为false。

 1/** Stack used to manage the current hierarchy of states */
2private StateInfo mStateStack[];
3
4/** Top of mStateStack */
5private int mStateStackTopIndex = -1;
6
7/** A temporary stack used to manage the state stack */
8private StateInfo mTempStateStack[];
9
10/** The top of the mTempStateStack */
11private int mTempStateStackCount;
12
13/** State used when state machine is halted */
14private HaltingState mHaltingState = new HaltingState();
15
16/** State used when state machine is quitting */
17private QuittingState mQuittingState = new QuittingState();

 

上边儿的代码中包含了一些重要字段

其中mStateStack用来存储当前state的继承树结构,由父类到子类,也就是最后一个元素是子类

mTempStateStack是用来存储将要转换的state的继承树结构,和mStateStack的存储顺序相反,是由子类到父类,也就是说第一个元素是子类。mStateStack是通过mTempStateStack逆序得来的

 1/**
2* Move the contents of the temporary stack to the state stack
3* reversing the order of the items on the temporary stack as
4* they are moved.
5*
6* @return index into mStateStack where entering needs to start
7*/

8 private final int moveTempStateStackToStateStack() {
9      int startingIndex = mStateStackTopIndex + 1;
10      int i = mTempStateStackCount - 1;
11      int j = startingIndex;
12      while (i >= 0) {
13          if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
14               mStateStack[j] = mTempStateStack[i];
15               j += 1;
16               i -= 1;
17          }
18
19          mStateStackTopIndex = j - 1;
20          if (mDbg) {
21              mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
22                        + ",startingIndex=" + startingIndex + ",Top="
23                        + mStateStack[mStateStackTopIndex].state.getName());
24          }
25          return startingIndex;
26      }

 

那么mTempStateStack哪儿来的呢?有两处

一处是在stateMachine调用start后,会触发setupInitalStateStack方法对mTempStateStack进行一次赋值

 1/**
2* Initialize StateStack to mInitialState.
3*/

4private final void setupInitialStateStack() {
5     if (mDbg) {
6         mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName());
7     }
8
9     StateInfo curStateInfo = mStateInfo.get(mInitialState);
10    for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
11          mTempStateStack[mTempStateStackCount] = curStateInfo;
12          curStateInfo = curStateInfo.parentStateInfo;
13    }
14
15    // Empty the StateStack
16     mStateStackTopIndex = -1;
17
18     moveTempStateStackToStateStack();
19 }

 

因为mTempStateStack存储的是要进入的下一个状态的继承树,所以第二处对mTempStateStack赋值的地方就是在进入到下一个状态前进行一次赋值

 1 /**
2  * Setup the mTempStateStack with the states we are going to enter.
3 *
4 * This is found by searching up the destState's ancestors for a
5 * state that is already active i.e. StateInfo.active == true.
6 * The destStae and all of its inactive parents will be on the
7 * TempStateStack as the list of states to enter.
8 *
9 * @return StateInfo of the common ancestor for the destState and
10 * current state or null if there is no common parent.
11 */

12 private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
13       /**
14        * Search up the parent list of the destination state for an active
15        * state. Use a do while() loop as the destState must always be entered
16        * even if it is active. This can happen if we are exiting/entering
17        * the current state.
18        */

19         mTempStateStackCount = 0;
20         StateInfo curStateInfo = mStateInfo.get(destState);
21         do {
22              mTempStateStack[mTempStateStackCount++] = curStateInfo;
23              curStateInfo = curStateInfo.parentStateInfo;
24         } while ((curStateInfo != null) && !curStateInfo.active);
25
26         if (mDbg) {
27              mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
28                        + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
29         }
30         return curStateInfo;
31 }

 

也就是说状态机利用两个数组来记录当前state的继承树和下一个要切换的状态的继承树,并在状态切换时触发当前state数组中所有state对应的exit和下一个要切换到的state数组中所有state对应的enter。由此来实现对各个状态的管理

分析完之后发现状态机其实远比想想中简单多了,不过就是处理好多个状态切换,仅此而已。

但貌似StateMachine这个是被hide的也就是说不会封装在SDK中,三方应用无法使用。而且基本上平常不会用到,这个原因是什么呢?

翻墙看了一下,说是因为平常的应用刚开始设计时没有这么多状态不需要状态机这种机制,但等后续慢慢状态增多后想用状态机时发现,代码重构的话耗费太大,所以也就放弃了这个状态机机制,也许吧,目前不可知

https://mp.weixin.qq.com/s/admWhpUk7nwuZ8hBds1mNw