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 @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 @Override public void enter() {
269 log("mP1.enter");
270 }
271 @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 @Override public void exit() {
290 log("mP1.exit");
291 }
292 }
293
294 class S1 extends State {
295 @Override public void enter() {
296 log("mS1.enter");
297 }
298 @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 @Override public void exit() {
310 log("mS1.exit");
311 }
312 }
313
314 class S2 extends State {
315 @Override public void enter() {
316 log("mS2.enter");
317 }
318 @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 @Override public void exit() {
338 log("mS2.exit");
339 }
340 }
341
342 class P2 extends State {
343 @Override public void enter() {
344 log("mP2.enter");
345 sendMessage(obtainMessage(CMD_5));
346 }
347 @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 @Override public void exit() {
361 log("mP2.exit");
362 }
363 }
364
365 @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 */
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 @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 @Override public void enter() {
269 log("mP1.enter");
270 }
271 @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 @Override public void exit() {
290 log("mP1.exit");
291 }
292 }
293
294 class S1 extends State {
295 @Override public void enter() {
296 log("mS1.enter");
297 }
298 @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 @Override public void exit() {
310 log("mS1.exit");
311 }
312 }
313
314 class S2 extends State {
315 @Override public void enter() {
316 log("mS2.enter");
317 }
318 @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 @Override public void exit() {
338 log("mS2.exit");
339 }
340 }
341
342 class P2 extends State {
343 @Override public void enter() {
344 log("mP2.enter");
345 sendMessage(obtainMessage(CMD_5));
346 }
347 @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 @Override public void exit() {
361 log("mP2.exit");
362 }
363 }
364
365 @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
- Android StateMachine 学习笔记
- Android StateMachine与State学习
- 安卓StateMachine学习笔记--待续
- Android学习 StateMachine与State模式
- Android学习 StateMachine与State模式
- Android学习 StateMachine与State模式
- Android学习 StateMachine与State模式
- Android学习 StateMachine与State模式
- Android学习 StateMachine与State模式
- Android学习 StateMachine与State模式(状态机)
- Android学习 StateMachine与State模式
- Android学习 StateMachine与State模式
- Android学习 StateMachine与State模式
- Android wifi-framework StateMachine和AsyncChannel 学习
- android状态机机制StateMachine
- android状态机机制StateMachine
- Android StateMachine分析
- android状态机statemachine详解
- 类像“一台数据引擎”
- Apache 403 Forbidden:You don't have permission to access / on this server.
- hadoop入门教程-hadoop是什么
- java中单项链表实现方法:增加、删除、插入数据
- Ubuntu 12.4 配置android开发环境(转载)
- Android StateMachine 学习笔记
- 码农小汪-剑指Offer之26 -数组中出现次数超过一半的数字
- initramfs的运行过程
- poj1716 Integer Intervals--单源最短路径&差分约束
- 深度优先搜索算法的通用解法
- URAL 1786 Sandro's Biography
- Ubuntu 12.04安装arm-linux-gcc-4.4.3
- UVAlive 4683 Find The Number
- 引jquery入库