2017-05-01 | learn

Android StateMachine

默认构造函数创建一个 HandlerThread 维护消息队列
并根据传入的 Looper 创建 SmHandler 对象

Constructor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
protected StateMachine(String name) {
mSmThread = new HandlerThread(name);
mSmThread.start();
Looper looper = mSmThread.getLooper();

initStateMachine(name, looper);
}

/**
* Constructor creates a StateMachine using the looper.
*
* @param name of the state machine
*/
protected StateMachine(String name, Looper looper) {
initStateMachine(name, looper);
}

/**
* Constructor creates a StateMachine using the handler.
*
* @param name of the state machine
*/
protected StateMachine(String name, Handler handler) {
initStateMachine(name, handler.getLooper());
}

/**
* Initialize.
*
* @param looper for this state machine
* @param name of the state machine
*/
private void initStateMachine(String name, Looper looper) {
mName = name;
mSmHandler = new SmHandler(looper, this);
}

StateMachine::addState(State state, State parent)

-> SmHandler::addState(State state, State parent)

  • 添加 State: State 保存在 HashMap<State, StateInfo> mStateInfo
  • StateInfo : 保存 State , parentStateInfo 还有 active 标志位
  • 做 parent 唯一检测
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private final StateInfo addState(State state, State parent) {
...

StateInfo parentStateInfo = null;
if (parent != null) {
parentStateInfo = mStateInfo.get(parent);
if (parentStateInfo == null) {
// Recursively add our parent as it's not been added yet.
parentStateInfo = addState(parent, null);
}
}
StateInfo stateInfo = mStateInfo.get(state);
if (stateInfo == null) {
stateInfo = new StateInfo();
mStateInfo.put(state, stateInfo);
}

// Validate that we aren't adding the same state in two different hierarchies.
if ((stateInfo.parentStateInfo != null)
&& (stateInfo.parentStateInfo != parentStateInfo)) {
throw new RuntimeException("state already added");
}
stateInfo.state = state;
stateInfo.parentStateInfo = parentStateInfo;
stateInfo.active = false;
if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
return stateInfo;
}

img

SmHandler::handleMessage(Message msg)

  • 处理接收到的 Message
  • 保存 msg 到 SmHandler.mMsg
  • 调用 SmHandler::processMsg(msg)
  • 把消费掉 msg 的 State 保存到 msgProcessedState
  • 调用 SmHandler::performTransitions(State msgProcessedState, Message msg)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final void handleMessage(Message msg) {
if (mHasQuit) {
return;
}

/** onPreHandleMessage **/
...

/** Save the current message */
mMsg = msg;

/** State that processed the message */
State msgProcessedState = null;
if (mIsConstructionCompleted) {
/** Normal path */
msgProcessedState = processMsg(msg);
} ...

performTransitions(msgProcessedState, msg);

/** onPostHandleMessage **/
...
}

SmHandler::processMsg(Message msg)

  • 把消息交给当前 State 处理 if NOT_HANDLED -> StateInfo.parentState.state.processMessage(msg) -> SmHandler::unhandledMessage(msg)
  • 返回消费掉 msg 的 State
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private final State processMsg(Message msg) {
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];

...

if (isQuit(msg)) {
transitionTo(mQuittingState);
} else {
while (!curStateInfo.state.processMessage(msg)) {
/**
* Not processed
*/
curStateInfo = curStateInfo.parentStateInfo;
if (curStateInfo == null) {
/**
* No parents left so it's not handled
*/
mSm.unhandledMessage(msg);
break;
}
...
}
}
return (curStateInfo != null) ? curStateInfo.state : null;
}

SmHandler::performTransitions(msgProcessedState, msg)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
                S0
/ \
S1 S4
/ \ \
S2 S3 S5 <-- init

mStateStack -> {S0,S4,S5}

transitionTo(S3)

after setupTempStateStackWithStatesToEnter
mTempStateStack -> {S3,S1,S0} & return S0

after invokeExitMethods(S0)
mStateStack -> {S0,S4.active=false,S5.active=false} & mStateStackTopIndex = {index of S0}

after moveTempStateStackToStateStack
mStateStack -> {S0,S1.active=false,S3.active=false} & return {index of S0} + 1

after invokeEnterMethods
mStateStack -> {S0,S1.active=true,S3.active=true}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
private void performTransitions(State msgProcessedState, Message msg) {
/**
* If transitionTo has been called, exit and then enter
* the appropriate states. We loop on this to allow
* enter and exit methods to use transitionTo.
*/
State orgState = mStateStack[mStateStackTopIndex].state;

/**
* Record whether message needs to be logged before we transition and
* and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which
* always set msg.obj to the handler.
*/
boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);

if (mLogRecords.logOnlyTransitions()) {
/** Record only if there is a transition */
if (mDestState != null) {
mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
orgState, mDestState);
}
} else if (recordLogMsg) {
/** Record message */
mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,
mDestState);
}

State destState = mDestState;
if (destState != null) {
/**
* Process the transitions including transitions in the enter/exit methods
*/
while (true) {
...

/**
* Determine the states to exit and enter and return the
* common ancestor state of the enter/exit states. Then
* invoke the exit methods then the enter methods.
*/
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
invokeExitMethods(commonStateInfo);
int stateStackEnteringIndex = moveTempStateStackToStateStack();
invokeEnterMethods(stateStackEnteringIndex);

/**
* Since we have transitioned to a new state we need to have
* any deferred messages moved to the front of the message queue
* so they will be processed before any other messages in the
* message queue.
*/
moveDeferredMessageAtFrontOfQueue();

if (destState != mDestState) {
// A new mDestState so continue looping
destState = mDestState;
} else {
// No change in mDestState so we're done
break;
}
}
mDestState = null;
}

/**
* After processing all transitions check and
* see if the last transition was to quit or halt.
*/
if (destState != null) {
if (destState == mQuittingState) {
/**
* Call onQuitting to let subclasses cleanup.
*/
mSm.onQuitting();
cleanupAfterQuitting();
} else if (destState == mHaltingState) {
/**
* Call onHalting() if we've transitioned to the halting
* state. All subsequent messages will be processed in
* in the halting state which invokes haltedProcessMessage(msg);
*/
mSm.onHalting();
}
}
}
SmHandler::setupTempStateStackWithStatesToEnter(State destState)

将 destState 到 null / 第一个 State.active == true 之间路径上的所有 State 保存到 mTempStateStack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
/**
* Search up the parent list of the destination state for an active
* state. Use a do while() loop as the destState must always be entered
* even if it is active. This can happen if we are exiting/entering
* the current state.
*/
mTempStateStackCount = 0;
StateInfo curStateInfo = mStateInfo.get(destState);
do {
mTempStateStack[mTempStateStackCount++] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
} while ((curStateInfo != null) && !curStateInfo.active);

...

return curStateInfo;
}

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package com.example.kang.statemachine;

import android.os.Message;
import com.example.kang.statemachine.util.State;
import com.example.kang.statemachine.util.StateMachine;

/**
* Created by kang on 17-3-9.
*
* S0
* / \
* S1 S4
* / \ \
* S2 S3 S5 <-- init
*/
public class ExampleStateMachine extends StateMachine {
private static final int CMD_0 = 0x00;
private static final int CMD_1 = 0x01;
private static final int CMD_2 = 0x02;
private static final int CMD_3 = 0x03;
private static final int CMD_4 = 0x04;
private static final int CMD_5 = 0x05;

private static final int CMD_TRANSITION_TO_S3 = 0x06;
private static final int CMD_TRANSITION_TO_S2 = 0x07;
private static final int CMD_TRANSITION_TO_S5 = 0x08;

public ExampleStateMachine(String name) {
super(name);

addState(s0);
addState(s1,s0);
addState(s2,s1);
addState(s3,s1);
addState(s4,s0);
addState(s5,s4);

setInitialState(s5);
}

public void transitionToS3(){
sendMessage(CMD_TRANSITION_TO_S3);
}

public void transitionToS2(){
sendMessage(CMD_TRANSITION_TO_S2);
}

public void transitionToS5(){
sendMessage(CMD_TRANSITION_TO_S5);
}

private State s0 = new NameState("S0");
private State s1 = new NameState("S1");
private State s2 = new NameState("S2"){
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_0:
super.processMessage(msg);
break;
case CMD_TRANSITION_TO_S5:
sendMessage(CMD_1);
deferMessage(obtainMessage(CMD_0));
transitionTo(s5);
break;
default:
return super.processMessage(msg);
}
return HANDLED;
}
};
private State s3 = new NameState("S3"){
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_TRANSITION_TO_S2:
deferMessage(obtainMessage(CMD_0));
transitionTo(s2);
break;
default:
return super.processMessage(msg);
}
return HANDLED;
}
};
private State s4 = new NameState("S4");
private State s5 = new NameState("S5"){
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_TRANSITION_TO_S3:
transitionTo(s3);
break;
default:
super.processMessage(msg);
}
return HANDLED;
}
};

private static class NameState extends State {
String name;

public NameState(String name) {
this.name = name;
}

@Override
public boolean processMessage(Message msg) {
System.out.println(name + " processMessage --> " + msg.toString());
return super.processMessage(msg);
}

@Override
public void enter() {
super.enter();
System.out.println("enter --> " + name);
}

@Override
public void exit() {
super.exit();
System.out.println("exit --> " + name);
}
}
}

OUTPUT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#Init
I/System.out: enter --> S0
I/System.out: enter --> S4
I/System.out: enter --> S5

#stateMachine.transitionToS3();
I/System.out: exit --> S5
I/System.out: exit --> S4
I/System.out: enter --> S1
I/System.out: enter --> S3

#stateMachine.transitionToS2(); -> deferMessage(obtainMessage(CMD_0)) + transition
I/System.out: exit --> S3
I/System.out: enter --> S2
I/System.out: S2 processMessage --> { ... what=0 target=... }

#stateMachine.transitionToS5(); -> sendMessage(CMD_1) + deferMessage(obtainMessage(CMD_0)); + transition
I/System.out: exit --> S2
I/System.out: exit --> S1
I/System.out: enter --> S4
I/System.out: enter --> S5
I/System.out: S5 processMessage --> { ... what=0 target=... }
I/System.out: S5 processMessage --> { ... what=1 target=... }