接上页(http://www.linuxidc.com/Linux/2013-08/88700.htm)接下来先看下状态转移performTransitions方法,首先碰到mDestState,这是SmHandler里面的一个标记,指向当前状态路径最下面的一个状态,后面都是父状态。可以在其他时候调用private final void transitionTo(IState destState)方法(外面的接口方法)改变当前mDestState的指向。这样处理完当前消息之后,performTransitions就会根据最新的mDestState来进行状态路径切换。状态切换有点像树的遍历一样,并不是回到根节点在进行搜索到新的节点,而是会回到原来的mDestState和最新的mDestState最近的一个共同祖先,然后在走向新的mDestState状态。进行状态切换主要是执行原状态路径上需要退出的状态的exit()方法,和新的状态路径上的enter()方法。
最后看下状态机的初始化invokeEnterMethods,这个直接看状态机的实例比较方便。看一个简单一些的应用,蓝牙APK里面关于耳机和电话连接处理的HeadsetStateMachine类,其构造方法中,关于状态机的代码如下:
addState(mDisconnected);
addState(mPending);
addState(mConnected);
addState(mAudioOn);
setInitialState(mDisconnected);
以上两块代码,第一块是建立本状态机的整个框架,就相当于建立整个状态树,第二个是设置初始化状态。再看看HeadsetStateMachine中的静态代码块
static HeadsetStateMachine make(HeadsetService context) {
Log.d(TAG, "make");
HeadsetStateMachine hssm = new HeadsetStateMachine(context);
hssm.start();
return hssm;
}
以上两块代码,第一块是建立本状态机的整个框架,就相当于建立整个状态树,第二个是设置初始化状态。再看看HeadsetStateMachine中的静态代码块
这里面有一个start()方法,从这个方法开始,状态机开始运作,包括分配内存,根据初始化状态设置初始化状态路径,这一点主要在setupInitialStateStack方法中执行,依次执行状态路径上每个状态的enter方法,这个使用了消息机制sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));,最终就在本段开头的invokeEnterMethods方法中执行。
到这里状态机主要内容基本讲解完毕,貌似绝大多数都需要记忆,记住了感觉就理解到了。:-D 有点像本文开头说的。初一看感觉没有什么,但是如果想象下你有一个这样的需求,耳机和手机的状态一直在切换,你会采用什么方式去做,在考虑了很多之后会感觉状态机真的是一个很厉害的东西。:-D 接下来附上Android源码中的demo,为了方便理解,笔者将输出增加了一些空行,多余的空行不是demo打印的。
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();
}
*
If this is executed by sending two messages CMD_1 and CMD_2
* (Note the synchronize is only needed because we use hsm.wait())
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());
}
}
输出如下:
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


被折叠的 条评论
为什么被折叠?



