State machine

来源:互联网 发布:私密空间软件 编辑:程序博客网 时间:2024/05/19 22:02

在看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内部调用。

下面的例子用一个拥有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;     }}INITIALstate 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,转换到mS2D/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会唤醒阻塞的线程。