Android StateMachine 学习笔记

来源:互联网 发布:域名必须实名认证吗 编辑:程序博客网 时间:2024/06/05 22:55

通过阅读Google代码文档来学习 StateMachine

其实Google的代码文档对StateMachine介绍已经很详细了,详细阅读之后所了解的东西,完全可以应付使用。

首先先贴出StateMachine的介绍文档,后面我会根据自己的理解来进行翻译和解释,相信看完这篇文章可以对StateMachine有一定的了解。英文介绍比较长,如果不是很想花时间来阅读,可以直接看翻译后的部分。
36/**
37 * {@hide}
38 *
39 * <p>The state machine defined here is a hierarchical state machine which processes messages
40 * and can have states arranged hierarchically.</p>
41 *
42 * <p>A state is a <code>State</code> object and must implement
43 * <code>processMessage</code> and optionally <code>enter/exit/getName</code>.
44 * The enter/exit methods are equivalent to the construction and destruction
45 * in Object Oriented programming and are used to perform initialization and
46 * cleanup of the state respectively. The <code>getName</code> method returns the
47 * name of the state the default implementation returns the class name it may be
48 * desirable to have this return the name of the state instance name instead.
49 * In particular if a particular state class has multiple instances.</p>
50 *
51 * <p>When a state machine is created <code>addState</code> is used to build the
52 * hierarchy and <code>setInitialState</code> is used to identify which of these
53 * is the initial state. After construction the programmer calls <code>start</code>
54 * which initializes and starts the state machine. The first action the StateMachine
55 * is to the invoke <code>enter</code> for all of the initial state's hierarchy,
56 * starting at its eldest parent. The calls to enter will be done in the context
57 * of the StateMachines Handler not in the context of the call to start and they
58 * will be invoked before any messages are processed. For example, given the simple
59 * state machine below mP1.enter will be invoked and then mS1.enter. Finally,
60 * messages sent to the state machine will be processed by the current state,
61 * in our simple state machine below that would initially be mS1.processMessage.</p>

62<code>
63        mP1
64       /   \
65      mS2   mS1 ----> initial state
66</code>
67 * <p>After the state machine is created and started, messages are sent to a state
68 * machine using <code>sendMessage</code> and the messages are created using
69 * <code>obtainMessage</code>. When the state machine receives a message the
70 * current state's <code>processMessage</code> is invoked. In the above example
71 * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code>
72 * to change the current state to a new state</p>
73 *
74 * <p>Each state in the state machine may have a zero or one parent states and if
75 * a child state is unable to handle a message it may have the message processed
76 * by its parent by returning false or NOT_HANDLED. If a message is never processed
77 * <code>unhandledMessage</code> will be invoked to give one last chance for the state machine
78 * to process the message.</p>

79 *
80 * <p>When all processing is completed a state machine may choose to call
81 * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code>
82 * returns the state machine will transfer to an internal <code>HaltingState</code>
83 * and invoke <code>halting</code>. Any message subsequently received by the state
84 * machine will cause <code>haltedProcessMessage</code> to be invoked.</p>
85 *
86 * <p>If it is desirable to completely stop the state machine call <code>quit</code> or
87 * <code>quitNow</code>. These will call <code>exit</code> of the current state and its parents,
88 * call <code>onQuiting</code> and then exit Thread/Loopers.</p>
89 *
90 * <p>In addition to <code>processMessage</code> each <code>State</code> has
91 * an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p>
92 *
93 * <p>Since the states are arranged in a hierarchy transitioning to a new state
94 * causes current states to be exited and new states to be entered. To determine 
95 * the list of states to be entered/exited the common parent closest to 
96 * the current state is found. We then exit from the current state and its 
97 * parent's up to but not including the common parent state and then enter all 
98 * of the new states below the common parent down to the destination state.
99 * If there is no common parent all states are exited and then the new states
100 * are entered.</p>
101 *
102 * <p>Two other methods that states can use are <code>deferMessage</code> and
103 * <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends
104 * a message but places it on the front of the queue rather than the back. The
105 * <code>deferMessage</code> causes the message to be saved on a list until a
106 * transition is made to a new state. At which time all of the deferred messages
107 * will be put on the front of the state machine queue with the oldest message
108 * at the front. These will then be processed by the new current state before
109 * any other messages that are on the queue or might be added later. Both of
110 * these are protected and may only be invoked from within a state machine.</p>
111 *
112 * <p>To illustrate some of these properties we'll use state machine with an 8
113 * state hierarchy:</p>
114<code>
115          mP0
116         /   \
117        mP1   mS0
118       /   \
119      mS2   mS1
120     /  \    \
121    mS3  mS4  mS5  ---> initial state
122</code>
123 * <p>After starting mS5 the list of active states is mP0, mP1, mS1 and mS5. <---------------------need to verify
124 * So the order of calling processMessage when a message is received is mS5,
125 * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this
126 * message by returning false or NOT_HANDLED.</p>
127 *
128 * <p>Now assume mS5.processMessage receives a message it can handle, and during
129 * the handling determines the machine should change states. It could call
130 * transitionTo(mS4) and return true or HANDLED. Immediately after returning from
131 * processMessage the state machine runtime will find the common parent,
132 * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then
133 * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So
134 * when the next message is received mS4.processMessage will be invoked.</p>
135 *
136 * <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine.
137 * It responds with "Hello World" being printed to the log for every message.</p>
138<code>
139class HelloWorld extends StateMachine {
140    HelloWorld(String name) {
141        super(name);
142        addState(mState1);
143        setInitialState(mState1);
144    }
145
146    public static HelloWorld makeHelloWorld() {
147        HelloWorld hw = new HelloWorld("hw");
148        hw.start();
149        return hw;
150    }
151
152    class State1 extends State {
153        &#64;Override public boolean processMessage(Message message) {
154            log("Hello World");
155            return HANDLED;
156        }
157    }
158    State1 mState1 = new State1();
159}
160
161void testHelloWorld() {
162    HelloWorld hw = makeHelloWorld();
163    hw.sendMessage(hw.obtainMessage());
164}
165</code>
166 * <p>A more interesting state machine is one with four states
167 * with two independent parent states.</p>
168<code>
169        mP1      mP2
170       /   \
171      mS2   mS1
172</code>
173 * <p>Here is a description of this state machine using pseudo code.</p>
174 <code>
175state mP1 {
176     enter { log("mP1.enter"); }
177     exit { log("mP1.exit");  }
178     on msg {
179         CMD_2 {
180             send(CMD_3);
181             defer(msg);
182             transitonTo(mS2);
183             return HANDLED;
184         }
185         return NOT_HANDLED;
186     }
187}
188
189INITIAL
190state mS1 parent mP1 {
191     enter { log("mS1.enter"); }
192     exit  { log("mS1.exit");  }
193     on msg {
194         CMD_1 {
195             transitionTo(mS1);
196             return HANDLED;
197         }
198         return NOT_HANDLED;
199     }
200}
201
202state mS2 parent mP1 {
203     enter { log("mS2.enter"); }
204     exit  { log("mS2.exit");  }
205     on msg {
206         CMD_2 {
207             send(CMD_4);
208             return HANDLED;
209         }
210         CMD_3 {
211             defer(msg);
212             transitionTo(mP2);
213             return HANDLED;
214         }
215         return NOT_HANDLED;
216     }
217}
218
219state mP2 {
220     enter {
221         log("mP2.enter");
222         send(CMD_5);
223     }
224     exit { log("mP2.exit"); }
225     on msg {
226         CMD_3, CMD_4 { return HANDLED; }
227         CMD_5 {
228             transitionTo(HaltingState);
229             return HANDLED;
230         }
231         return NOT_HANDLED;
232     }
233}
234</code>
235 * <p>The implementation is below and also in StateMachineTest:</p>
236<code>
237class Hsm1 extends StateMachine {
238    public static final int CMD_1 = 1;
239    public static final int CMD_2 = 2;
240    public static final int CMD_3 = 3;
241    public static final int CMD_4 = 4;
242    public static final int CMD_5 = 5;
243
244    public static Hsm1 makeHsm1() {
245        log("makeHsm1 E");
246        Hsm1 sm = new Hsm1("hsm1");
247        sm.start();
248        log("makeHsm1 X");
249        return sm;
250    }
251
252    Hsm1(String name) {
253        super(name);
254        log("ctor E");
255
256        // Add states, use indentation to show hierarchy
257        addState(mP1);
258            addState(mS1, mP1);
259            addState(mS2, mP1);
260        addState(mP2);
261
262        // Set the initial state
263        setInitialState(mS1);
264        log("ctor X");
265    }
266
267    class P1 extends State {
268        &#64;Override public void enter() {
269            log("mP1.enter");
270        }
271        &#64;Override public boolean processMessage(Message message) {
272            boolean retVal;
273            log("mP1.processMessage what=" + message.what);
274            switch(message.what) {
275            case CMD_2:
276                // CMD_2 will arrive in mS2 before CMD_3
277                sendMessage(obtainMessage(CMD_3));
278                deferMessage(message);
279                transitionTo(mS2);
280                retVal = HANDLED;
281                break;
282            default:
283                // Any message we don't understand in this state invokes unhandledMessage
284                retVal = NOT_HANDLED;
285                break;
286            }
287            return retVal;
288        }
289        &#64;Override public void exit() {
290            log("mP1.exit");
291        }
292    }
293
294    class S1 extends State {
295        &#64;Override public void enter() {
296            log("mS1.enter");
297        }
298        &#64;Override public boolean processMessage(Message message) {
299            log("S1.processMessage what=" + message.what);
300            if (message.what == CMD_1) {
301                // Transition to ourself to show that enter/exit is called
302                transitionTo(mS1);
303                return HANDLED;
304            } else {
305                // Let parent process all other messages
306                return NOT_HANDLED;
307            }
308        }
309        &#64;Override public void exit() {
310            log("mS1.exit");
311        }
312    }
313
314    class S2 extends State {
315        &#64;Override public void enter() {
316            log("mS2.enter");
317        }
318        &#64;Override public boolean processMessage(Message message) {
319            boolean retVal;
320            log("mS2.processMessage what=" + message.what);
321            switch(message.what) {
322            case(CMD_2):
323                sendMessage(obtainMessage(CMD_4));
324                retVal = HANDLED;
325                break;
326            case(CMD_3):
327                deferMessage(message);
328                transitionTo(mP2);
329                retVal = HANDLED;
330                break;
331            default:
332                retVal = NOT_HANDLED;
333                break;
334            }
335            return retVal;
336        }
337        &#64;Override public void exit() {
338            log("mS2.exit");
339        }
340    }
341
342    class P2 extends State {
343        &#64;Override public void enter() {
344            log("mP2.enter");
345            sendMessage(obtainMessage(CMD_5));
346        }
347        &#64;Override public boolean processMessage(Message message) {
348            log("P2.processMessage what=" + message.what);
349            switch(message.what) {
350            case(CMD_3):
351                break;
352            case(CMD_4):
353                break;
354            case(CMD_5):
355                transitionToHaltingState();
356                break;
357            }
358            return HANDLED;
359        }
360        &#64;Override public void exit() {
361            log("mP2.exit");
362        }
363    }
364
365    &#64;Override
366    void onHalting() {
367        log("halting");
368        synchronized (this) {
369            this.notifyAll();
370        }
371    }
372
373    P1 mP1 = new P1();
374    S1 mS1 = new S1();
375    S2 mS2 = new S2();
376    P2 mP2 = new P2();
377}
378</code>
379 * <p>If this is executed by sending two messages CMD_1 and CMD_2
380 * (Note the synchronize is only needed because we use hsm.wait())</p>
381<code>
382Hsm1 hsm = makeHsm1();
383synchronize(hsm) {
384     hsm.sendMessage(obtainMessage(hsm.CMD_1));
385     hsm.sendMessage(obtainMessage(hsm.CMD_2));
386     try {
387          // wait for the messages to be handled
388          hsm.wait();
389     } catch (InterruptedException e) {
390          loge("exception while waiting " + e.getMessage());
391     }
392}
393</code>
394 * <p>The output is:</p>
395<code>
396D/hsm1    ( 1999): makeHsm1 E
397D/hsm1    ( 1999): ctor E
398D/hsm1    ( 1999): ctor X
399D/hsm1    ( 1999): mP1.enter
400D/hsm1    ( 1999): mS1.enter
401D/hsm1    ( 1999): makeHsm1 X
402D/hsm1    ( 1999): mS1.processMessage what=1
403D/hsm1    ( 1999): mS1.exit
404D/hsm1    ( 1999): mS1.enter
405D/hsm1    ( 1999): mS1.processMessage what=2
406D/hsm1    ( 1999): mP1.processMessage what=2
407D/hsm1    ( 1999): mS1.exit
408D/hsm1    ( 1999): mS2.enter
409D/hsm1    ( 1999): mS2.processMessage what=2
410D/hsm1    ( 1999): mS2.processMessage what=3
411D/hsm1    ( 1999): mS2.exit
412D/hsm1    ( 1999): mP1.exit
413D/hsm1    ( 1999): mP2.enter
414D/hsm1    ( 1999): mP2.processMessage what=3
415D/hsm1    ( 1999): mP2.processMessage what=4
416D/hsm1    ( 1999): mP2.processMessage what=5
417D/hsm1    ( 1999): mP2.exit
418D/hsm1    ( 1999): halting
419</code>
420 */


2. 翻译&总结,解释的不好的地方可以指正出来,共同学习。
状态机被定义为一个可以状态分层的处理消息并且可以让状态分层级排列的机器。

一个状态对象必须实现processMessage并且有选择的实现enter/exit/getName这三个方法。
方法enter/exit在面向对象编程中相当于构造和析构的概念,分别被用来进行初始化工作和结束时清理状态的工作。
getName方法返回状态的名字,默认的实现是返回类名,但是让它返回状态实例的名字可能是更想要的,
特别是在一个特殊的状态类会有多个实例情况下。(最后这句话我也想不到到底咋回事)

当一个状态机被创建后,addState被用来建立层级结构,setInitialState被用来定义这些状态的初始状态。
构造完成之后,工程师调用start方法来初始化并且运行状态机。
第一步的动作是从最高(老)的父节点开始,给所有初始状态层级调用enter方法(大概意思是一层调用一个 ?)
所有的enter的调用会通过状态机Handler的背景下完成而不是通过start方法来实现(有点乱),并且是在任何消息被处理前调用的。
举个例子,#62-65的图,状态机开始后调用 enter的流程,mP1.enter ->mS1.enter。
最后,发给状态的消息会被当前状态处理,在图中会被mS1.processMessage处理。

在状态机被创建并启动之后,用sendMessage可以给状态机发送消息,用obtainMessage可以创建一条消息。
当状态机收到一条消息后,当前状态的processMessage方法会被调用来处理这条消息。
在上面的例子中,mS1.processMessage会被先调用(看来还有后掉用的,不是处理不了就扔掉或者怎么样),
在一个状态中可以通过transitionTo方法来把当前的状态转到新的状态上。

状态机中的每个状态都有0个或者1个父状态,并且如果子状态不能够处理消息,那么消息可能被它的父状态已返回false或者NOT_HANDLED来处理。
如果一个消息从来没有被处理过,unhandledMessage将会给状态机提供最后一次处理这条消息的机会。

当消息处理完成,状态机可能会选择调用transitionToHaltingState。
当当前的processingMessage(这个是啥方法?)返回,状态机将转换到内部状态HaltingState并且调用Halting方法。
随后,状态机接收的任何消息都会引起haltedProcessMessage的调用。

如果想完全的停止状态机可以调用quit或者quitNow。
这些方法会调用当前状态的exit和它的父状态的onQuiting(可能有误,自己验证吧),之后退出Looper

除了processMessage之外,每个状态有enter/exit方法可能被重写。

因为状态被排列成层级的结构,转换到一个新的状态会引起当前的状态退出,和新的状态进入这两个过程。
通过找到当前状态和最终状态最近的共同父状态,来决定要进入和退出的状态清单。
从当前状态退出并向上一直到当前共同父状态,但是不包括父状态。
之后,向下进入所有在共同父状态下面的新的状态一直到终状态。
如果没有共同父状态,所有状态退出后,新的状态直接进入

另外两个状态可以用的方法是deferMessage,sendMessageAtFrontOfQueue。
sendMessageAtFrontOfQueue 发送一条消息,但是被放在消息队列的前面而不是后面。
deferMessage导致消息被存到一个清单中,之后转到一个新的状态。
所有的被延期的消息将会被放在状态机的消息队列的老的消息的前面。
这些消息将会在新的状态中,在任何其他消息之前被执行。
这两个方法都是protected的,可能仅仅只能从状态机内部调用。

为了阐述清楚上面的一些特点,下面将会用一个八个状态的状态机来举例。
下面的过程还是很简单的,比较容易看懂。用的都是上面提到的点。


以上就是对状态机的初步学习,相信看完翻译总结部分就可以开始使用啦。
这里有一篇讲的很好的文章
http://blog.csdn.net/pi9nc/article/details/27503071



0 0
原创粉丝点击