状态机

定义了一套分层处理消息的状态机,具有按层次排列的状态。

State对象必须实现processMessage方法,可以按需实现enter/exit/getName。enter/exit方法可等价于面向对象编码中的构造和销毁,会用做状态的初始化和回收。getName方法返回了状态的名称,方法的默认实现返回的是类的名称。让getName方法返回状态实例的名称可能更满足要求,特别是在一个状态类中有多个状态实例。

当一个状态记被创建,addState方法用来建造这个层次,setInitialState方法识别这些状态中的初始状态。构造完以后通过start方法初始化和启动状态机。状态机的第一个动作是递归调用初始状态的enter,从初始状态的最大父节点开始。enter是在状态机的handler中被完成,而不是在调用调用enter的上下文中,并且在任何消息被处理之前被调用。举个简单的例子:

    mP1
   /   \
  mS2   mS1 ---initial state

mP1的enter首先调用,然后是mS1的enter;最终发送到状态机的消息被当前状态处理,在这个例子中就是mS1.processMessage。

状态记被创建和启动以后,通过obtainMessage方法创建消息,通过使用sendMessage发给状态机。当状态机收到消息,当前状态的processMessage被调用。在上面的例子中,首先mS1.processMessage被调用,然后通过transition改变到新的状态。

状态机中的每个状态都可能有0个或1个父节点,如果一个子节点状态返回false/NOT_HANDLED不能处理消息,可以让父节点状态处理这个消息。如果一个消息在子节点状态和任意祖先节点状态都没有被处理,unhandledMessage被调用,作为状态机处理消息的最后一次机会。

当所有流程都处理完成时,状态机可以选择调用transitionToHaltingState。当前processingMessage返回时,状态机变换到内部的HaltingState,并且调用halting方法。以后状态机收到的任何消息都会触发haltedProcessMessage被调用。

如果要求完全是停止状态机,调用quit/quitNow方法,这些都会调用当前状态及其祖先的exit方法,调用onQuiiting, 然后退出线程/消息循环队列。

除了processMessage方法外, 每个状态还有enter方法和exit方法可以被重写。

既然这些状态是按层次排序的,变换到一个新的状态会出发当前状态的推出和新状态的进入。为了确定进入/推出的状态列表,与当前状态最相近的共同父节点会被发现,然后从当前状态及其向上的祖先退出,但不包含共同父节点状态,然后进入共同父节点之下的状态,直到目标状态。如果没有共同父节点状态,那么所有的状态都退出,然后新状态被进入。

状态可以使用其他两个方法是deferMessage和sendMessageAtFrontQueue。sendMessageAtFrontQueue发送一条消息放到队列的头部,而不是尾部。这个deferMessage方法触发消息保存在一个列表中,直到一次新状态转换的发生。此时,所有被推迟的消息都被放到状态机队列的前面,这些消息中最旧的消息放到最前面。这些消息就会被新的状态在队列消息之前被处理。这两种都是收到保护的,只会在状态机内部被调用。

为了示例一些属性,使用8个状态层次的状态机:

      mP0
     /  \mS0
    mP1
   /  \
  mS2  mS1
 /  \    \
mS3  mS4  mS5 ---inittial state

启动mS5之后,激活的状态列表是mP0, mP1, mS1, mS5。因此当收到一条消息时,调用processMessage的顺序是mS5, mS1, mP1, mP0,假设每个processMessage都返回false或者NOT_HANDLED,表明不能处理这些消息。

现在假设mS5收到了一条消息,并且自己可以处理,在处理过程中确认需要变换状态。可以调用transitionTo(mS4),然后返回true/HANDLED。从processMessage返回后,状态机立刻就找共同的父节点状态mP1,之后调用mS5.exit, mS1.exit, mS2.enter, mS4.enter。新激活状态列表是mP0, mP1, mS2, mS4。因此当下一条消息被接收到mS4.processMessage就会被调用。

介绍几个更具体的例子,下面是一个经典的HelloWorld状态机,每一条消息都会回应一行“helloWorld”打印。

class HelloWorld extends StateMachine {
   
    HelloWorld(String name) {
   
        super(name);
        addState(mState1);
        setInitialState(mState1);
    }

    public static HelloWorld makeHelloWorld() {
   
        HelloWorld hw = new HelloWorld("hw");
        hw.start();
        return hw;
    }

    class State1 extends State {
   
       @Override public boolean processMessage(Message message) {
   
            log("Hello World");
            return HANDLED;
        }
    }
    State1 mState1 = new State1();
}

void testHelloWorld() {
   
    HelloWorld hw = makeHelloWorld();
    hw.sendMessage(hw.obtainMessage());
}

下面是一个更有意思的状态机,一共有4个状态,有2个独立的父节点状态。

  mP1       mP2
 /  \
mS2  mS1 ---initial state

先看伪代码实现:

state mP1 {
   
     enter {
    log("mP1.enter"); }
     exit {
    log("mP1.exit");  }
     on msg {
   
         CMD_2 {
   
             send(CMD_3);
             defer(msg);
             transitionTo(mS2);
             return HANDLED
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值