分享

Android StateMachine分析

 android之情殇 2014-02-19
  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例子:
  1. <code>  
  2. class HelloWorld extends StateMachine {  
  3.     HelloWorld(String name) {  
  4.         super(name);  
  5.         addState(mState1);  
  6.         setInitialState(mState1);  
  7.     }  
  8.   
  9.     public static HelloWorld makeHelloWorld() {  
  10.         HelloWorld hw = new HelloWorld("hw");  
  11.         hw.start();  
  12.         return hw;  
  13.     }  
  14.   
  15.     class State1 extends State {  
  16.         @Override public boolean processMessage(Message message) {  
  17.             Log.d(TAG, "Hello World");  
  18.             return HANDLED;  
  19.         }  
  20.     }  
  21.     State1 mState1 = new State1();  
  22. }  
  23.   
  24. void testHelloWorld() {  
  25.     HelloWorld hw = makeHelloWorld();  
  26.     hw.sendMessage(hw.obtainMessage());  
  27. }  
  28. </code>  
  29.  * <p>A more interesting state machine is one with four states  
  30.  * with two independent parent states.</p>  
  31. <code>  
  32.         mP1      mP2  
  33.        /   \  
  34.       mS2   mS1  
  35. </code>  
  36.  * <p>Here is a description of this state machine using pseudo code.</p>  
  37.  <code>  
  38.   
  39. state mP1 {  
  40.      enter { log("mP1.enter"); }  
  41.      exit { log("mP1.exit");  }  
  42.      on msg {  
  43.          CMD_2 {  
  44.              send(CMD_3);  
  45.              defer(msg);  
  46.              transitonTo(mS2);  
  47.              return HANDLED;  
  48.          }  
  49.          return NOT_HANDLED;  
  50.      }  
  51. }  
  52.   
  53. INITIAL  
  54. state mS1 parent mP1 {  
  55.      enter { log("mS1.enter"); }  
  56.      exit  { log("mS1.exit");  }  
  57.      on msg {  
  58.          CMD_1 {  
  59.              transitionTo(mS1);  
  60.              return HANDLED;  
  61.          }  
  62.          return NOT_HANDLED;  
  63.      }  
  64. }  
  65.   
  66. state mS2 parent mP1 {  
  67.      enter { log("mS2.enter"); }  
  68.      exit  { log("mS2.exit");  }  
  69.      on msg {  
  70.          CMD_2 {  
  71.              send(CMD_4);  
  72.              return HANDLED;  
  73.          }  
  74.          CMD_3 {  
  75.              defer(msg);  
  76.              transitionTo(mP2);  
  77.              return HANDLED;  
  78.          }  
  79.          return NOT_HANDLED;  
  80.      }  
  81. }  
  82.   
  83. state mP2 {  
  84.      enter {  
  85.          log("mP2.enter");  
  86.          send(CMD_5);  
  87.      }  
  88.      exit { log("mP2.exit"); }  
  89.      on msg {  
  90.          CMD_3, CMD_4 { return HANDLED; }  
  91.          CMD_5 {  
  92.              transitionTo(HaltingState);  
  93.              return HANDLED;  
  94.          }  
  95.          return NOT_HANDLED;  
  96.      }  
  97. }  
  98. </code>  
  99.  * <p>The implementation is below and also in StateMachineTest:</p>  
  100. <code>  
  101. class Hsm1 extends StateMachine {  
  102.     private static final String TAG = "hsm1";  
  103.   
  104.     public static final int CMD_1 = 1;  
  105.     public static final int CMD_2 = 2;  
  106.     public static final int CMD_3 = 3;  
  107.     public static final int CMD_4 = 4;  
  108.     public static final int CMD_5 = 5;  
  109.   
  110.     public static Hsm1 makeHsm1() {  
  111.         Log.d(TAG, "makeHsm1 E");  
  112.         Hsm1 sm = new Hsm1("hsm1");  
  113.         sm.start();  
  114.         Log.d(TAG, "makeHsm1 X");  
  115.         return sm;  
  116.     }  
  117.   
  118.     Hsm1(String name) {  
  119.         super(name);  
  120.         Log.d(TAG, "ctor E");  
  121.   
  122.         // Add states, use indentation to show hierarchy  
  123.         addState(mP1);  
  124.             addState(mS1, mP1);  
  125.             addState(mS2, mP1);  
  126.         addState(mP2);  
  127.    
  128.         // Set the initial state  
  129.         setInitialState(mS1);  
  130.         Log.d(TAG, "ctor X");  
  131.     }  
  132.   
  133.     class P1 extends State {  
  134.         @Override public void enter() {  
  135.             Log.d(TAG, "mP1.enter");  
  136.         }  
  137.         @Override public boolean processMessage(Message message) {  
  138.             boolean retVal;  
  139.             Log.d(TAG, "mP1.processMessage what=" + message.what);  
  140.             switch(message.what) {  
  141.             case CMD_2:  
  142.                 // CMD_2 will arrive in mS2 before CMD_3  
  143.                 sendMessage(obtainMessage(CMD_3));  
  144.                 deferMessage(message);  
  145.                 transitionTo(mS2);  
  146.                 retVal = HANDLED;  
  147.                 break;  
  148.             default:  
  149.                 // Any message we don't understand in this state invokes unhandledMessage  
  150.                 retVal = NOT_HANDLED;  
  151.                 break;  
  152.             }  
  153.             return retVal;  
  154.         }  
  155.         @Override public void exit() {  
  156.             Log.d(TAG, "mP1.exit");  
  157.         }  
  158.     }  
  159.   
  160.     class S1 extends State {  
  161.         @Override public void enter() {  
  162.             Log.d(TAG, "mS1.enter");  
  163.         }  
  164.         @Override public boolean processMessage(Message message) {  
  165.             Log.d(TAG, "S1.processMessage what=" + message.what);  
  166.             if (message.what == CMD_1) {  
  167.                 // Transition to ourself to show that enter/exit is called  
  168.                 transitionTo(mS1);  
  169.                 return HANDLED;  
  170.             } else {  
  171.                 // Let parent process all other messages  
  172.                 return NOT_HANDLED;  
  173.             }  
  174.         }  
  175.         @Override public void exit() {  
  176.             Log.d(TAG, "mS1.exit");  
  177.         }  
  178.     }  
  179.   
  180.     class S2 extends State {  
  181.         @Override public void enter() {  
  182.             Log.d(TAG, "mS2.enter");  
  183.         }  
  184.         @Override public boolean processMessage(Message message) {  
  185.             boolean retVal;  
  186.             Log.d(TAG, "mS2.processMessage what=" + message.what);  
  187.             switch(message.what) {  
  188.             case(CMD_2):  
  189.                 sendMessage(obtainMessage(CMD_4));  
  190.                 retVal = HANDLED;                                                                                                                                                            
  191.                 break;  
  192.             case(CMD_3):  
  193.                 deferMessage(message);  
  194.                 transitionTo(mP2);  
  195.                 retVal = HANDLED;  
  196.                 break;  
  197.             default:  
  198.                 retVal = NOT_HANDLED;  
  199.                 break;  
  200.             }  
  201.             return retVal;  
  202.         }  
  203.         @Override public void exit() {  
  204.             Log.d(TAG, "mS2.exit");  
  205.         }  
  206.     }  
  207.   
  208.     class P2 extends State {  
  209.         @Override public void enter() {  
  210.             Log.d(TAG, "mP2.enter");  
  211.             sendMessage(obtainMessage(CMD_5));  
  212.         }  
  213.         @Override public boolean processMessage(Message message) {                                                                                                                       
  214.             Log.d(TAG, "P2.processMessage what=" + message.what);  
  215.             switch(message.what) {  
  216.             case(CMD_3):  
  217.                 break;  
  218.             case(CMD_4):  
  219.                 break;  
  220.             case(CMD_5):  
  221.                 transitionToHaltingState();  
  222.                 break;  
  223.             }  
  224.             return HANDLED;  
  225.         }  
  226.         @Override public void exit() {  
  227.             Log.d(TAG, "mP2.exit");  
  228.         }  
  229.     }  
  230.   
  231.     @Override  
  232.     void onHalting() {  
  233.         Log.d(TAG, "halting");  
  234.         synchronized (this) {  
  235.             this.notifyAll();  
  236.         }  
  237.     }  
  238.   
  239.     P1 mP1 = new P1();  
  240.     S1 mS1 = new S1();  
  241.     S2 mS2 = new S2();  
  242.     P2 mP2 = new P2();  
  243. }  
  244.   
  245. </code>  
  246.  * <p>If this is executed by sending two messages CMD_1 and CMD_2  
  247.  * (Note the synchronize is only needed because we use hsm.wait())</p>  
  248. <code>  
  249. Hsm1 hsm = makeHsm1();  
  250. synchronize(hsm) {  
  251.      hsm.sendMessage(obtainMessage(hsm.CMD_1));  
  252.      hsm.sendMessage(obtainMessage(hsm.CMD_2));  
  253.      try {  
  254.           // wait for the messages to be handled  
  255.           hsm.wait();  
  256.      } catch (InterruptedException e) {  
  257.           Log.e(TAG, "exception while waiting " + e.getMessage());  
  258.      }  
  259. }  
  260. </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>

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多