State machine

在看state machine代码(StateMachine.java)的时候,发现源码的注释写的很好; 前半部分是讲解,后半部分是示例代码,读过之后state machine的要点也就基本掌握了。今天就做下翻译。当然翻译的意义并不大,能看英文的还是直接看英文的好,这里只是给自己练练手。言归正传,原文如下:


这里定义的是一个分层次的状态机,可以处理消息,并且拥有预制的分层状态。

状态是一个State对象,必须实现processMessage方法,选择性的实现enter/exit/getName方法。enter/exit就相当于面向对象编程里的构造和析构方法,分别用于初始化和清理状态对象。getName方法可以返回状态对象的名字; 默认实现是返回类名。建议实现getName方法返回state实例的名字,特别是一个state类有多个实例的情况。

当一个state machine被创建的时候,addState方法用于创建状态的层次结构,setInitialState方法用于确定初始状态。完成构造之后可以通过调用start方法初始化,并开启这个state machine。StateMachine的第一个动作是调用所有层次状态的enter方法,并从它的根父状态开始。enter方法调用的完成是在StateMachine的handler中,而不是在start方法的调用中,并且这些enter方法的调用是在所有消息被处理之前。例如,下面例子中的mP1.enter首先被调用,然后是mS1.enter被调用。最终,发往这个state machine的消息会被当前状态处理, 即下面例子中的初始状态mS1的precessMessage方法。

   mP1
  /   \
 mS2  mS1 --->initial state

当state machine被创建完成,并start后,消息可以通过obtainMessage方法创建,并通过sendMessage方法发往state machine。当state machine接收到消息后,当前状态的processMessage方法会被调用。 在上面的例子中,mS1.processMessage会被第一个调用。当前状态可以通过transitonTo方法转往新状态。

state machine中的每个状态可以没有或者有一个父状态。 如果一个子状态不能够处理消息,那么可以返回false 或者Not_HANDLED, 交由它的父状态处理。如果一个消息没有被任何状态处理,那么unhandledMessage方法会被调用,这是state machine处理这个消息的最后机会。

当所有的处理任务完成之后, state machine可以选择调用transitionToHaltingState方法。当 当前状态的processMessage方法返回后,state machine将会转换到Halting中间状态,并且调用onHalting方法。此后state machine收到的所有消息都会导致haltedProcessMessage方法被调用。

完全停止state machine可以调用quit或者quitNow方法。这两个方法会调用当前状态和它父状态的exit方法,然后onQutting方法会被调用,最后退出Thread/Loopers。

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

由于所有的状态是分层预制的,转换到一个新状态会引起当前状态退出,并且进入新状态。靠近当前状态的通用父状态(多个状态的父状态)被用于确定应该进入/退出的状态序列。首先从当前状态退出,并依次退出父状态,直到遇到通用父状态,然后依次进入这个通用父状态下的子状态,直到进入目标状态。如果没有通用父状态,所有的状态都会退出,然后直接进入目标新状态。

状态可以调用的另外两个方法是deferMessage和sendMessageAtFrontOfQueue。sendMessageAtFrontOfQueue方法可以发送一个消息,并将它放在队列的首位,而不是末尾。deferMessage方法的调用会导致message被放进一个list中,直到转换到新状态; 等转换到新状态后所有被延迟的消息都会被放到state machine消息队列的前部。这些消息会被新状态优先处理。deferMessage方法在Android N 上还是protected类型, 在Android O上是public类型; sendMessageAtFrontOfQueue方法在Android O上依然是protected类型, 只可以在state machine内部调用。

扫描二维码关注公众号,回复: 4265004 查看本文章

下面的例子用一个拥有8个状态的state machine来说明了上面介绍的特点:

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

当开启mS5状态后,mP0, mP1, mS1和mS5都会处于活跃状态。所以当收到消息的时候,如果每个状态的processMessage方法都无法处理这个消息而返回false或则NOT_HANDLED,那么processMessage方法被调用的顺序是mS5, mS1, mP1,mP0。

现在假设mS5.processMessage接收到一个它可以处理的消息,而且在处理的过程中确定要转换到新状态。mS5.processMessage可以调用transitionTo(mS4),并且返回true或者HANDLED。当从processMessage方法返回后,state machine的runtime会立刻找到通用父状态,即mP1。然后state machine会调用mS5.exit,mS1.exit,后续调用mS2.enter和mS4.enter。现在活跃的状态是mP0, mP1,mS2和mS4。所以当收到下一个消息的时候,mS4.processMessage会被调用。

下面是HelloWorld式的具体例子, 每个消息都会打印”Hello World”。

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());
}

下面的state machine更有趣,他有4个状态,却有两个相互独立的父状态。

  mP1  mP2
 /   \
ms2 mS1

下面用伪代码对这个state machine做了描述。

state mP1 {
     enter { log("mP1.enter"); }
     exit { log("mP1.exit");  }
     on msg {
         CMD_2 {
             send(CMD_3);
             defer(msg);
             transitionTo(mS2);
             return HANDLED;
         }
         return NOT_HANDLED;
     }
}

INITIAL
state mS1 parent mP1 {
     enter { log("mS1.enter"); }
     exit  { log("mS1.exit");  }
     on msg {
         CMD_1 {
             transitionTo(mS1);
             return HANDLED;
         }
         return NOT_HANDLED;
     }
}

state mS2 parent mP1 {
     enter { log("mS2.enter"); }
     exit  { log("mS2.exit");  }
     on msg {
         CMD_2 {
             send(CMD_4);
             return HANDLED;
         }
         CMD_3 {
             defer(msg);
             transitionTo(mP2);
             return HANDLED;
         }
         return NOT_HANDLED;
     }
}

state mP2 {
     enter {
         log("mP2.enter");
         send(CMD_5);
     }
     exit { log("mP2.exit"); }
     on msg {
         CMD_3, CMD_4 { return HANDLED; }
         CMD_5 {
             transitionTo(HaltingState);
             return HANDLED;
         }
         return NOT_HANDLED;
     }
}

下面代码是具体实现,StateMachineTest也有这部分代码:

class Hsm1 extends StateMachine {
    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 Hsm1 makeHsm1() {
        log("makeHsm1 E");
        Hsm1 sm = new Hsm1("hsm1");
        sm.start();
        log("makeHsm1 X");
        return sm;
    }

    Hsm1(String name) {
        super(name);
        log("ctor E");

        // Add states, use indentation to show hierarchy
        addState(mP1);//添加状态mP1
        addState(mS1, mP1);//添加状态mS1,mP1为其父状态
        addState(mS2, mP1);//添加状态mS2,mP1为其父状态
        addState(mP2);//添加状态mP2

        // Set the initial state
        setInitialState(mS1);//将mS1设置为初始状态
        log("ctor X");
    }

    class P1 extends State {//状态类P1
        @Override public void enter() {
            log("mP1.enter");
        }
        @Override public boolean processMessage(Message message) {
            boolean retVal;
            log("mP1.processMessage what=" + message.what);
            switch(message.what) {
            case CMD_2:
                // CMD_2 will arrive in mS2 before CMD_3
                sendMessage(obtainMessage(CMD_3));//发送消息CMD_3
                deferMessage(message);//延迟CMD_2,转换到状态mS2处理
                transitionTo(mS2);//转换到mS2状态
                retVal = HANDLED;
                break;
            default:
                // Any message we don't understand in this state invokes unhandledMessage
                retVal = NOT_HANDLED;
                break;
            }
            return retVal;
        }
        @Override public void exit() {
            log("mP1.exit");
        }
    }

    class S1 extends State {//状态类S1
        @Override public void enter() {
            log("mS1.enter");
        }
        @Override public boolean processMessage(Message message) {
            log("S1.processMessage what=" + message.what);
            if (message.what == CMD_1) {//收到消息CMD_1,就转换到mS1状态,这有意思,退出再进来一次
                // Transition to ourself to show that enter/exit is called
                transitionTo(mS1);//转换到mS1,重新进mS1
                return HANDLED;
            } else {//否则不处理,返回NOT_HANDLED
                // Let parent process all other messages
                return NOT_HANDLED;
            }
        }
        @Override public void exit() {
            log("mS1.exit");
        }
    }

    class S2 extends State {//状态类S2
        @Override public void enter() {
            log("mS2.enter");
        }
        @Override public boolean processMessage(Message message) {
            boolean retVal;
            log("mS2.processMessage what=" + message.what);
            switch(message.what) {
            case(CMD_2)://收到CMD_2消息后发送CMD_4消息
                sendMessage(obtainMessage(CMD_4));
                retVal = HANDLED;
                break;
            case(CMD_3)://收到CMD_3消息后,延迟该消息,转换到状态mP2后处理
                deferMessage(message);
                transitionTo(mP2);
                retVal = HANDLED;
                break;
            default:
                retVal = NOT_HANDLED;
                break;
            }
            return retVal;
        }
        @Override public void exit() {
            log("mS2.exit");
        }
    }

    class P2 extends State {//状态类P2
        @Override public void enter() {
            log("mP2.enter");
            sendMessage(obtainMessage(CMD_5));//进入该状态就发送CMD_5消息
        }
        @Override public boolean processMessage(Message message) {
            log("P2.processMessage what=" + message.what);
            switch(message.what) {
            case(CMD_3):
                break;
            case(CMD_4):
                break;
            case(CMD_5)://收到CMD_5消息后转换到Halting状态
                transitionToHaltingState();
                break;
            }
            return HANDLED;
        }
        @Override public void exit() {
            log("mP2.exit");
        }
    }

    @Override
    void onHalting() {
        log("halting");
        synchronized (this) {
            this.notifyAll();//唤醒wait的线程
        }
    }

    //创建各个状态类的实例对象
    P1 mP1 = new P1();
    S1 mS1 = new S1();
    S2 mS2 = new S2();
    P2 mP2 = new P2();
}

如果在执行的过程中只发送CMD_1和CMD_2这两个消息(由于使用了hsm.wait(), 所以需要使用synchronize同步块,并且基于hsm):

Hsm1 hsm = makeHsm1();//构造并启动状态机
synchronize(hsm) {
     hsm.sendMessage(obtainMessage(hsm.CMD_1));//发送CMD_1消息
     hsm.sendMessage(obtainMessage(hsm.CMD_2));//发送CMD_2消息
     try {
          // wait for the messages to be handled
          hsm.wait();
     } catch (InterruptedException e) {
          loge("exception while waiting " + e.getMessage());
     }
}

输出如下:

D/hsm1    ( 1999): makeHsm1 E //makeHsm1()最先被调用,这个log第一个打印,即开始构造状态机
D/hsm1    ( 1999): ctor E //进入状态机Hsm1的构造函数
D/hsm1    ( 1999): ctor X //退出状态机Hsm1的构造函数
D/hsm1    ( 1999): mP1.enter  //Start状态机后,mS1作为初始状态,会引起其父状态enter方法被调用
D/hsm1    ( 1999): mS1.enter //mS1.enter被调用
D/hsm1    ( 1999): makeHsm1 X //状态机构造结束
D/hsm1    ( 1999): mS1.processMessage what=1 //状态mS1收到CMD_1消息,重新进入mS1状态
D/hsm1    ( 1999): mS1.exit //mS1.exit方法被调用
D/hsm1    ( 1999): mS1.enter //mS1.enter方法被调用。
D/hsm1    ( 1999): mS1.processMessage what=2 //mS1收到CMD_2消息,但是不处理,交由父状态mP1处理
D/hsm1    ( 1999): mP1.processMessage what=2 //mP1收到CMD_2消息;发送CMD_3,延迟CMD_2,转换到mS2
D/hsm1    ( 1999): mS1.exit //退出mS1状态,mS1.exit被调用
D/hsm1    ( 1999): mS2.enter //进入mS2状态,mS2.enter被调用
D/hsm1    ( 1999): mS2.processMessage what=2 //mS2优先处理CMD_2消息,发送CMD_4(队列中的排在CMD_3之后)
D/hsm1    ( 1999): mS2.processMessage what=3 //mS2收到CMD_3消息; 延迟CMD_3,并转换到mP2状态
D/hsm1    ( 1999): mS2.exit //退出mS2状态, mS2.exit方法被调用
D/hsm1    ( 1999): mP1.exit //退出mP1状态,mP1.exit方法被调用
D/hsm1    ( 1999): mP2.enter //进入mP2状态,mP2.enter方法被调用, 并发生CMD_5(队列中排在CMD_4之后)
D/hsm1    ( 1999): mP2.processMessage what=3 //mP2优先处理之前被延迟的CMD_3消息
D/hsm1    ( 1999): mP2.processMessage what=4 //处理CMD_4消息
D/hsm1    ( 1999): mP2.processMessage what=5 //处理CMD_5消息; 转入Halting状态
D/hsm1    ( 1999): mP2.exit //退出mP2状态,mP2.exit方法被调用。
D/hsm1    ( 1999): halting //onHalting方法被调用,在该方法中notifyAll会唤醒阻塞的线程。

猜你喜欢

转载自blog.csdn.net/Dylan_Sen/article/details/78471417