StateMachine解释
一个状态是一个State对象,必须实现processMessage接口,可以选择性的实现enter,exit,getName接口,StateMachine里面的enter和exit相当与面向对象编程里面的构造函数和析构函数,分别用来初始化和清除State对象,getName方法返回状态的名字,接口默认是返回class的名字,返回值一般用来描述该状态的实例化对象名字,特别是有些特殊的状态有多个实例化对象的时候。

当一个StateMachine创建的时候,addState用来创建层级,setInitialState用来确认哪个状态是初始状态,构造完毕之后,用户调用start方法来初始化和启动StateMachine,StatMachine的第一个动作是:从初始状态最顶层的父状态开始逐层调用enter方法,enter会在StateMachine的Handler环境中被执行,而不是在call的环境开始,enter会在所有message被执行前被调用,例如,给出一个简单的StateMachine,mP1的enter会被先调用,然后才是mS1的enter,最后message会被发送到StateMachine的当前状态执行。
        mP1
       /   \
      mS2   mS1 ----> initial state

当一个StateMachine被created和started之后,通过sendMessage向StateMachine发送message,message通过obtainMessage创建,当一个状态机收到message之后,当前状态的processMessage方法会被调用,在上面的例子里面,mS1的processMessage会被最先调用,可以通过transitionTo来把当前状态跳转到一个新的状态。

StateMachine里面的每个状态都有零个或一个父状态,如果子状态无法handle这个message,那么该message就会被父状态的processMessage执行,子状态返回false或者NOT_HANDLED,如果一个message永远无法执行,那么unhandledMessage会被调用,用来给message最后一次机会让StateMachine来执行。

当所有的进程完成,StateMachine会选择调用transitionToHaltingState,当current processingMessage返回时,StateMachine会跳转到内部的HaltingState和调用halting,任何后来的message都会触发haltedProcessMessage的调用。

如果StateMachine正常的退出,可以调用quit或者abort,调用quit会退出当前状态和他的父状态,调用onQuiting之后退出Thread和Loopers

不仅仅是processMessage,一般每个状态都会复写一个enter和一个exit接口

自从所有的状态都被层次的安排好了之后,跳转到一个新的状态会导致当前状态的exited和新状态的entered,决定列出当前状态和同一个父亲的兄弟状态的entered/exited,我们然后退出当前状态,父状态唤起,但是不包括同一个父状态的其他兄弟状态,然后层次遍历的进入从当前父状态到新状态的所有状态,如果新状态和当前状态不共父状态,那么当前状态和他的所有父状态都会退出,然后进入新状态。

另外两个可以用到的方法是deferMessage和sendMessageAtFrontOfQueue,sendMessageAtFrontOfQueue发送message,但是会把message放到队列的头部,而不是末尾,deferMessage会把message保存到一个list里面,直到跳转到新的状态,然后最先被defferd message会被放到StateMachine队列的头部,这些message会被当前状态最先执行,然后才是队列里面的其他后来的message,这两个方法是protecte,只能在StateMachine的内部调用

为了说明这些特性,我用一个带有8个状态的StateMachine来举个例子
      

mP0 

          /   \ 

         mP1   mS0 

        /   \ 

       mS2   mS1 

      /  \    \ 

     mS3  mS4  mS5  ---> initial state



当mS5被叫起来之后,当前活跃的状态依次是mP0,mP1,mS1和mS5,所以当mS5接收到一个message之后,假如所有的processMessage都没有handle这个message,调用processMessage的顺序一次是mS5,mS1,mP1,mP0

假如现在mS5的processMessage收到一个能handle的message,如果handle的结果是跳转到其他状态,可以通过call transitionTo(mS4),返回true或者HANDLED,当processMessage返回后,StateMachine会找到他们的公共父状态,也就是mP1,然后会调用mS5的exit,mS1的exit,mS2的enter,mS4的enter,然后新的活跃状态表就是mP0,mP1,mS2,mS4,因此当下一个message收到后,mS4的enter会被调用。

接下来我们来一些实际的例子,这里是一个经典的StateMachine HelloWorld例子:

<code>
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.d(TAG, "Hello World");
            return HANDLED;
        }
    }
    State1 mState1 = new State1();
}

void testHelloWorld() {
    HelloWorld hw = makeHelloWorld();
    hw.sendMessage(hw.obtainMessage());
}
</code>
 * <p>A more interesting state machine is one with four states
 * with two independent parent states.</p>
<code>
        mP1      mP2
       /   \
      mS2   mS1
</code>
 * <p>Here is a description of this state machine using pseudo code.</p>
 <code>

state mP1 {
     enter { log("mP1.enter"); }
     exit { log("mP1.exit");  }
     on msg {
         CMD_2 {
             send(CMD_3);
             defer(msg);
             transitonTo(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;
     }
}
</code>
 * <p>The implementation is below and also in StateMachineTest:</p>
<code>
class Hsm1 extends StateMachine {
    private static final String TAG = "hsm1";

    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.d(TAG, "makeHsm1 E");
        Hsm1 sm = new Hsm1("hsm1");
        sm.start();
        Log.d(TAG, "makeHsm1 X");
        return sm;
    }

    Hsm1(String name) {
        super(name);
        Log.d(TAG, "ctor E");

        // Add states, use indentation to show hierarchy
        addState(mP1);
            addState(mS1, mP1);
            addState(mS2, mP1);
        addState(mP2);
 
        // Set the initial state
        setInitialState(mS1);
        Log.d(TAG, "ctor X");
    }

    class P1 extends State {
        @Override public void enter() {
            Log.d(TAG, "mP1.enter");
        }
        @Override public boolean processMessage(Message message) {
            boolean retVal;
            Log.d(TAG, "mP1.processMessage what=" + message.what);
            switch(message.what) {
            case CMD_2:
                // CMD_2 will arrive in mS2 before CMD_3
                sendMessage(obtainMessage(CMD_3));
                deferMessage(message);
                transitionTo(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.d(TAG, "mP1.exit");
        }
    }

    class S1 extends State {
        @Override public void enter() {
            Log.d(TAG, "mS1.enter");
        }
        @Override public boolean processMessage(Message message) {
            Log.d(TAG, "S1.processMessage what=" + message.what);
            if (message.what == CMD_1) {
                // Transition to ourself to show that enter/exit is called
                transitionTo(mS1);
                return HANDLED;
            } else {
                // Let parent process all other messages
                return NOT_HANDLED;
            }
        }
        @Override public void exit() {
            Log.d(TAG, "mS1.exit");
        }
    }

    class S2 extends State {
        @Override public void enter() {
            Log.d(TAG, "mS2.enter");
        }
        @Override public boolean processMessage(Message message) {
            boolean retVal;
            Log.d(TAG, "mS2.processMessage what=" + message.what);
            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.d(TAG, "mS2.exit");
        }
    }

    class P2 extends State {
        @Override public void enter() {
            Log.d(TAG, "mP2.enter");
            sendMessage(obtainMessage(CMD_5));
        }
        @Override public boolean processMessage(Message message) {                                                                                                                     
            Log.d(TAG, "P2.processMessage 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.d(TAG, "mP2.exit");
        }
    }

    @Override
    void onHalting() {
        Log.d(TAG, "halting");
        synchronized (this) {
            this.notifyAll();
        }
    }

    P1 mP1 = new P1();
    S1 mS1 = new S1();
    S2 mS2 = new S2();
    P2 mP2 = new P2();
}

</code>
 * <p>If this is executed by sending two messages CMD_1 and CMD_2
 * (Note the synchronize is only needed because we use hsm.wait())</p>
<code>
Hsm1 hsm = makeHsm1();
synchronize(hsm) {
     hsm.sendMessage(obtainMessage(hsm.CMD_1));
     hsm.sendMessage(obtainMessage(hsm.CMD_2));
     try {
          // wait for the messages to be handled
          hsm.wait();
     } catch (InterruptedException e) {
          Log.e(TAG, "exception while waiting " + e.getMessage());
     }
}
</code>
* <p>The output is:</p> 

 <code> 

 D/hsm1    ( 1999): makeHsm1 E 

 D/hsm1    ( 1999): ctor E 

 D/hsm1    ( 1999): ctor X 

 D/hsm1    ( 1999): mP1.enter 

 D/hsm1    ( 1999): mS1.enter 

 D/hsm1    ( 1999): makeHsm1 X 

 D/hsm1    ( 1999): mS1.processMessage what=1 

 D/hsm1    ( 1999): mS1.exit 

 D/hsm1    ( 1999): mS1.enter 

 D/hsm1    ( 1999): mS1.processMessage what=2 

 D/hsm1    ( 1999): mP1.processMessage what=2 

 D/hsm1    ( 1999): mS1.exit 

 D/hsm1    ( 1999): mS2.enter 

 D/hsm1    ( 1999): mS2.processMessage what=2 

 D/hsm1    ( 1999): mS2.processMessage what=3 

 D/hsm1    ( 1999): mS2.exit 

 D/hsm1    ( 1999): mP1.exit 

 D/hsm1    ( 1999): mP2.enter 

 D/hsm1    ( 1999): mP2.processMessage what=3 

 D/hsm1    ( 1999): mP2.processMessage what=4 

 D/hsm1    ( 1999): mP2.processMessage what=5 

 D/hsm1    ( 1999): mP2.exit 

 D/hsm1    ( 1999): halting 

 </code>