android状态机statemachine详解

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

android状态机statemachine详解
先说两句题外话,很感谢android,在这里能看到很多优秀的代码。

同时也感觉到外面的工程师真的很厉害,都是java人家就能写出这么牛的东西。

感慨之下就有了些思考:我们绝大多数人只要把那些牛人已经创造出来的牛逼的东西,记住并且弄懂就是一件非常不错的事情,至少能衣食无忧。

:-D 读书的时候需要经常做题,在理解的基础上记住解题方法基本就能很牛了,事实上高考中考绝大多数都是已经有过的题型,能做到前面所说的应该能进入不错的学校。

工作后,慢慢也发现很多了不起的技术,都是在国外已经发展的很成熟基础上学习过来的。

作为一个普通人,还是不要天天谈创新的好,hold不住,把基础的东西记住掌握即可。

说了一堆,也算聊以自慰。

我们知道类的成员可以分为两种:方法和属性。

大多数情况下,对于一个状态,比如某数大于0,类的方法都只能做出一种对应的操作,并且类的本身并不考虑外部状态。

android的状态机就属于大多数之后的那一小部分。

对于某个事件,或者更准确的说,某一个消息,在不同的状态下能做出不同的操作。

并且android状态机中的状态是继承的,就像数据结构中的树一样,如果当前节点(状态)不能对这个事件做出响应,就会到父节点继续判断并且做出响应,在下面的讲述中,我们称这个为状态路径,而对于所有状态称为状态树。

这一句话已经从整体上对状态机进行了概括,记住这些对后面的理解很有好处。

State,状态机中的状态封装类,这个类主要是实现了IState接口。

其中有状态的基本方法,enter,exit以及消息处理方法processMessage。

enter方法在状态机转入这个状态中会进行调用,exit方法在状态机转出这个方法时候会调用。

这里对于一个很简单的类,google使用了接口属性,说说自己的理解。

接口中的方法都是公有方法,并且只能声明常量。

将主要方法都放在接口中声明,一方面限制了方法的定义,一方面也突出了这个类主要就是拥有某种功能。

另外在State里面,声明了一个protect类型的构造方法,这样其他类就不可以直接声明state类的对象。

state在状态机statemachine类
里面是以StateInfo类型使用的,这个基本不影响访问。

statemachine里面主要工作都是由SmHandler类来完成的,statemachine本身绝大多数方法都是都是对SmHandler方法的再次封装。

另外为了能够做到及时响应主线程的消息,又声明了一个HandlerThread,主要任务都是在这个线程里面实现的。

现在直接去看SmHandler类吧,其最主要的方法就是handleMessage。

该方法的主要是三大模块,第一个消息处理,或者说是分配到对应的状态再有对应的状态进行处理比较合适,第二个状态的初始化,大概可以理解成执行初始化状态路径上每个状态的enter 方法,第三个执行状态转移,即更新状态树。

[java] view plaincopy
1.if (mIsConstructionCompleted) {
2./** Normal path */
3.processMsg(msg);//第一个消息处理
4.} else if (!mIsConstructionCompleted &&
5.(mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSm HandlerObj)) {
6./** Initial one time path. */
7.mIsConstructionCompleted = true;
8.invokeEnterMethods(0);//第二个状态的初始化
9.} else {
10.throw new RuntimeException("StateMachine.handleM essage: " +
11."The start method not called, received msg: " + msg);
12.}
13.performTransitions();//第三个执行状态转移
首先去看下processMsg方法
[java] view plaincopy
1.while (!curStateInfo.state.processMessage(msg)) {
2./**
3.* Not processed
4.*/
5.curStateInfo = curStateInfo.parentStateInfo;
6.if (curStateInfo == null) {
7./**
8.* No parents left so it's not handled
9.*/
10.mSm.unhandledMessage(msg);
11.break;
12.}
13.if (mDbg) {
14.Log.d(TAG, "processMsg: " + curStateInfo.state.getNa me());
15.}
16.}
从这段代码中(!curStateInfo.state.processMessage(msg))就说明了:如果当前状态执行完processMessage方法返回了false,也就是对当前消息NOT_HANDLED,那么就会持续调用这个状态的父状态执行方法。

一般终有一个状态能够处理消息的,如果真的没有处理,会记录到unhandledMessage方法里面的。

接下来先看下状态转移performTransitions方法,首先碰到mDestState,这是SmHandler里面的一个标记,指向当前状态路径最下面的一个状态,后面都是父状态。

可以在其他时候调用private final void transitionTo(IState destState)方法(外面的接口方法)改变当前mDestState的指向。

这样处理完当前消息之后,performTransitions就会根据最新的mDestState来进行状态路径切换。

状态切换有点像树的遍历一样,并不是回到根节点在进行搜索到新的节点,而是会回到原来的mDestState和最新的mDestState最近的一个共同祖先,然后在走向新的mDestState状态。

进行状态切换
主要是执行原状态路径上需要退出的状态的exit()方法,和新的状态路径上的enter()方法。

最后看下状态机的初始化invokeEnterMethods,这个直接看状态机的实例比较方便。

看一个简单一些的应用,蓝牙APK里面关于耳机和电话连接处理的HeadsetStateMachine类,其构造方法中,关于状态机的代码如下:
[java] view plaincopy
1.addState(mDisconnected);
2.addState(mPending);
3.addState(mConnected);
4.addState(mAudioOn);
5.
6.setInitialState(mDisconnected);
以上两块代码,第一块是建立本状态机的整个框架,就相当于建立整个状态树,第二个是设置初始化状态。

再看看HeadsetStateMachine中的静态代码块
[java] view plaincopy
1.static HeadsetStateMachine make(HeadsetService contex t) {
2.Log.d(TAG, "make");
3.HeadsetStateMachine hssm = new HeadsetStateMachine (context);
4.hssm.start();
5.return hssm;
6.}
以上两块代码,第一块是建立本状态机的整个框架,就相当于建立整个状态树,第二个是设置初始化状态。

再看看HeadsetStateMachine中的静态代码块
这里面有一个start()方法,从这个方法开始,状态机开始运作,包括分配内存,根据初始化状态设置初始化状态路径,这一点主要在
setupInitialStateStack方法中执行,依次执行状态路径上每个状态的enter方法,这个使用了消息机制sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));,最终就在本段开头的invokeEnterMethods方法中执行。

到这里状态机主要内容基本讲解完毕,貌似绝大多数都需要记忆,记住了感觉就理解到了。

:-D 有点像本文开头说的。

初一看感觉没有什么,但是如果想象下你有一个这样的需求,耳机和手机的状态一直在切换,你会采用什么方式去做,在考虑了很多之后会感觉状态机真的是一个很厉害的东西。

:-D 接下来附上android源码中的demo,为了方便理解,笔者将输出增加了一些空行,多余的空行不是demo打印的。

[java] view plaincopy
1.class Hsm1 extends StateMachine {
2.private static final String TAG = "hsm1";
3.
4.public static final int CMD_1 = 1;
5.public static final int CMD_2 = 2;
6.public static final int CMD_3 = 3;
7.public static final int CMD_4 = 4;
8.public static final int CMD_5 = 5;
9.
10.public static Hsm1 makeHsm1() {
11.Log.d(TAG, "makeHsm1 E");
12.Hsm1 sm = new Hsm1("hsm1");
13.sm.start();
14.Log.d(TAG, "makeHsm1 X");
15.return sm;
16.}
17.
18.Hsm1(String name) {
19.super(name);
20.Log.d(TAG, "ctor E");
21.
22.// Add states, use indentation to show hierarchy
23.addState(mP1);
24.addState(mS1, mP1);
25.addState(mS2, mP1);
26.addState(mP2);
27.
28.// Set the initial state
29.setInitialState(mS1);
30.Log.d(TAG, "ctor X");
31.}
32.
33.class P1 extends State {
34.@Override public void enter() {
35.Log.d(TAG, "mP1.enter");
36.}
37.@Override public boolean processMessage(Message message) {
38.boolean retVal;
39.Log.d(TAG, "mP1.processMessage what=" + message. what);
40.switch(message.what) {
41.case CMD_2:
42.// CMD_2 will arrive in mS2 before CMD_3
43.sendMessage(obtainMessage(CMD_3));
44.deferMessage(message);
45.transitionTo(mS2);
46.retVal = HANDLED;
47.break;
48.default:
49.// Any message we don't understand in this state invo kes unhandledMessage
50.retVal = NOT_HANDLED;
51.break;
52.}
53.return retVal;
54.}
55.@Override public void exit() {
56.Log.d(TAG, "mP1.exit");
57.}
58.}
59.
60.class S1 extends State {
61.@Override public void enter() {
62.Log.d(TAG, "mS1.enter");
63.}
64.@Override public boolean processMessage(Message message) {
65.Log.d(TAG, "S1.processMessage what=" + message.w hat);
66.if (message.what == CMD_1) {
67.// Transition to ourself to show that enter/exit is called
68.transitionTo(mS1);
69.return HANDLED;
70.} else {
71.// Let parent process all other messages
72.return NOT_HANDLED;
73.}
74.}
75.@Override public void exit() {
76.Log.d(TAG, "mS1.exit");
77.}
78.}
79.
80.class S2 extends State {
81.@Override public void enter() {
82.Log.d(TAG, "mS2.enter");
83.}
84.@Override public boolean processMessage(Message message) {
85.boolean retVal;
86.Log.d(TAG, "mS2.processMessage what=" + message. what);
87.switch(message.what) {
88.case(CMD_2):
89.sendMessage(obtainMessage(CMD_4));
90.retVal = HANDLED;
91.break;
92.case(CMD_3):
93.deferMessage(message);
94.transitionTo(mP2);
95.retVal = HANDLED;
96.break;
97.default:
98.retVal = NOT_HANDLED;
99.break;
101.return retVal;
102.}
103.@Override public void exit() {
104.Log.d(TAG, "mS2.exit");
105.}
106.}
107.
108.class P2 extends State {
109.@Override public void enter() {
110.Log.d(TAG, "mP2.enter");
111.sendMessage(obtainMessage(CMD_5));
112.}
113.@Override public boolean processMessage(Message message) {
114.Log.d(TAG, "P2.processMessage what=" + message.w hat);
115.switch(message.what) {
116.case(CMD_3):
117.break;
118.case(CMD_4):
119.break;
120.case(CMD_5):
121.transitionToHaltingState();
122.break;
123.}
124.return HANDLED;
125.}
126.@Override public void exit() {
127.Log.d(TAG, "mP2.exit");
129.}
130.
131.@Override
132.void onHalting() {
133.Log.d(TAG, "halting");
134.synchronized (this) {
135.this.notifyAll();
136.}
137.}
138.
139.P1 mP1 = new P1();
140.S1 mS1 = new S1();
141.S2 mS2 = new S2();
142.P2 mP2 = new P2();
143.}
144.</code>
145.* <p>If this is executed by sending two messages CM D_1 and CMD_2
146.* (Note the synchronize is only needed because we us e hsm.wait())</p>
147.<code>
148.Hsm1 hsm = makeHsm1();
149.synchronize(hsm) {
150.hsm.sendMessage(obtainMessage(hsm.CMD_1));
151.hsm.sendMessage(obtainMessage(hsm.CMD_2));
152.try {
153.// wait for the messages to be handled
154.hsm.wait();
155.} catch (InterruptedException e) {
156.Log.e(TAG, "exception while waiting " + e.getMessage ());
157.}
158.}
159.
160.
161.输出如下:
162.D/hsm1 ( 1999): makeHsm1 E
163.D/hsm1 ( 1999): ctor E
164.D/hsm1 ( 1999): ctor X
165.D/hsm1 ( 1999): mP1.enter
166.D/hsm1 ( 1999): mS1.enter
167.D/hsm1 ( 1999): makeHsm1 X
168.
169.D/hsm1 ( 1999): mS1.processMessage what=1
170.
171.D/hsm1 ( 1999): mS1.exit
172.D/hsm1 ( 1999): mS1.enter
173.
174.D/hsm1 ( 1999): mS1.processMessage what=2
175.D/hsm1 ( 1999): mP1.processMessage what=2
176.
177.D/hsm1 ( 1999): mS1.exit
178.D/hsm1 ( 1999): mS2.enter
179.
180.D/hsm1 ( 1999): mS2.processMessage what=2
181.D/hsm1 ( 1999): mS2.processMessage what=3
182.
183.D/hsm1 ( 1999): mS2.exit
184.D/hsm1 ( 1999): mP1.exit
185.D/hsm1 ( 1999): mP2.enter
186.
187.D/hsm1 ( 1999): mP2.processMessage what=3
188.D/hsm1 ( 1999): mP2.processMessage what=4
189.D/hsm1 ( 1999): mP2.processMessage what=5
190.D/hsm1 ( 1999): mP2.exit
191.D/hsm1 ( 1999): halting
Android状态机源码分析
分类:【Android 系统分析】2013-08-30 19:13 470人阅读评论(0) 收藏举报
在Android系统中,经常使用状态机来处理不同状态下的行为动作。

状态机是将对象的状态与行为封装在一起;可以解决庞大的分支语句带来程序阅读性差和不便于进行扩展问题,使整个结构变得更加清晰明了,降低程序管理的复杂性提高灵活度。

Android系统的StateMachine机制是一个State模式的应用,StateMachine是一个分层处理消息的状态机,并且是能够有分层排列状态。

构造状态机
StateMachine的构造函数都是protected类型,不能实例化,都是由其子类进行初始化操作。

StateMachine有两个重载的构造函数,一个是通过指定消息循环对象来构造状态机
[java] view plaincopy
1.protected StateMachine(String name, Looper looper) {
2.initStateMachine(name, looper);
3.}
另一个则是直接启动一个消息循环线程来构造一个状态机
[java] view plaincopy
1.protected StateMachine(String name) {
2.//启动一个消息循环线程
3.mSmThread = new HandlerThread(name);
4.mSmThread.start();
5.Looper looper = mSmThread.getLooper();
6.initStateMachine(name, looper);
7.}
这两种构造函数都会通过initStateMachine函数来初始化该状态机
[java] view plaincopy
1.private void initStateMachine(String name, Looper loope r) {
2.mName = name;
3.mSmHandler = new SmHandler(looper, this);
4.}
初始化过程比较简单,就是将状态机名称保存到成员变量mName中,同时创建SmHandler对象,SmHandler是一个Handle 对象,用于派发消息。

状态机中的每个状态使用State来封装,对于每个状态的信息又采用StateInfo来描述
StateMachine三个内部类:
1.ProcessedMessageInfo:保存已处理消息的信息;
2.ProcessedMessages:存储StateMachine最近处理的一些消息,需要保存最近处理的消息条数默认20,可以用户自己设定最大数目;
3.SmHandle是消息处理派发和状态控制切换的核心,运行在单独的线程上;
SmHandle成员变量定义:
ProcessedMessages用于保存已处理过的消息,mStateStack和mTempStateStack是一个数组栈,用于保存状态机中的链式状态关系。

mStateInfo定义为一个Hash链表,用于保存添加的所有状态。

mInitialState保存初始状态,mDestState保存切换的目的状态。

建立树形层次结构状态机
在构造完一个状态机前需要向状态机中添加各种状态,StateMachine提供了addState函数来添加状态
[java] view plaincopy
1.protected final void addState(State state, State parent) {
2.mSmHandler.addState(state, parent);
3.}
直接通过mSmHandler来完成状态添加过程
[java] view plaincopy
1.private final StateInfo addState(State state, State parent) {
2.if (mDbg) {
3.Log.d(TAG, "addStateInternal: E state=" + state.getName( )
4.+ ",parent=" + ((parent == null) ? "" : parent.getName()));
5.}
6.StateInfo parentStateInfo = null;
7.if (parent != null) {
8.parentStateInfo = mStateInfo.get(parent);
9.if (parentStateInfo == null) {
10.// Recursively add our parent as it's not been added y et.
11.parentStateInfo = addState(parent, null);
12.}
13.}
14.StateInfo stateInfo = mStateInfo.get(state);
15.if (stateInfo == null) {
16.stateInfo = new StateInfo();
17.mStateInfo.put(state, stateInfo);
18.}
19.
20.// Validate that we aren't adding the same state in two different hierarchies.
21.if ((stateInfo.parentStateInfo != null) &&
22.(stateInfo.parentStateInfo != parentStateInfo)) {
23.throw new RuntimeException("state already added");
24.}
25.stateInfo.state = state;
26.stateInfo.parentStateInfo = parentStateInfo;
27.stateInfo.active = false;
28.if (mDbg) Log.d(TAG, "addStateInternal: X stateInfo: " + stateInfo);
29.return stateInfo;
30.}
状态添加过程其实就是为每个State创建相应的StateInfo对象,通过该对象来建立各个状态之间的关系,并且一个State-StateInfo键值对的方式保存到mStateInfo Hash表中。

StateInfo就是包装State 组成一个Node,建立State的父子关系;mStateInfo =new HashMap<State, StateInfo>();用来保存State Machine中的所有State,可以按照树形层次结构组织状态机中的所有状态
接下来介绍构造以下树形状态机的过程:
[java] view plaincopy
1.sm.addState(S0,null);
2.sm.addState(S1,S0);
3.sm.addState(S2,S0);
4.sm.addState(S3,S1);
5.sm.addState(S4,S1);
6.sm.addState(S5,S2);
7.sm.addState(S6,S2);
8.sm.addState(S7,S2);
9.setInitialState(S4); //设置初始状态
1.添加根节点状态过程
对于根节点状态的加入sm.addState(S0,null)代码执行如下:[java] view plaincopy
1.private final StateInfo addState(S0,null)
2.{
3.StateInfo parentStateInfo = null;
4.//状态S0还未加入状态机中
5.StateInfo stateInfo = mStateInfo.get(state);
6.if (stateInfo == null) {
7.//创建State详细信息对象
8.stateInfo = new StateInfo();
9.//将S0加入状态机中
10.mStateInfo.put(state, stateInfo);
11.}
12.//设置状态S0的状态信息
13.stateInfo.state = state;
14.//S0的父节点为null
15.stateInfo.parentStateInfo = parentStateInfo;
16.stateInfo.active = false;
17.return stateInfo;
18.}
2.添加树枝节点状态过程
对于子节点S1状态的加入过程如下sm.addState(S1,S0):[java] view plaincopy
1.private final StateInfo addState(State state, State parent)
2.{
3.StateInfo parentStateInfo = null;
4.//状态S0在前面已经加入状态机中了
5.if (parent != null) {
6.//获取S0详细信息 StateInfo
7.parentStateInfo = mStateInfo.get(parent);
8.//因为S0已经存在于状态机中,因此这里不为空
9.if (parentStateInfo == null) {
10.//当前状态父状态未加入到StateMachine中,递归先加入其父State
11.parentStateInfo = addState(parent, null);
12.}
13.}
14.//从状态机中得到S1的状态信息,由于S1还为加入状态机,因此得到的结果为空
15.StateInfo stateInfo = mStateInfo.get(state);
16.if (stateInfo == null) {
17.//创建State详细信息对象
18.stateInfo = new StateInfo();
19.//将S1加入状态机中
20.mStateInfo.put(state, stateInfo);
21.}
22.//S1的状态信息刚创建,还没有为其设置父节点,因此其父节点为空
23.if ((stateInfo.parentStateInfo != null) &&
24.(stateInfo.parentStateInfo != parentStateInfo)) {
25.throw new RuntimeException("state already added");
26.}
27.//设置状态S1的状态信息
28.stateInfo.state = state;
29.//S1的父节点为S0
30.stateInfo.parentStateInfo = parentStateInfo;
31.stateInfo.active = false;
32.return stateInfo;
33.}
对于其他树枝节点的添加过程类似,这里不在介绍,最后保存在mStateInfo表中的所有状态之间就建立了以下树形关系:
启动状态机
当向状态机中添加完所有状态时,通过函数start函数来启动状态机
[java] view plaincopy
1.public void start() {
2.// mSmHandler can be null if the state machine has quit.
3.if (mSmHandler == null) return;
4./** Send the complete construction message */
pleteConstruction();
6.}
调用mSmHandler的completeConstruction函数来完成状态机的构造完成处理
[java] view plaincopy
1.private final void completeConstruction() {
2.if (mDbg) Log.d(TAG, "completeConstruction: E");
3.//查找状态树的深度
4.int maxDepth = 0;
5.for (StateInfo si : mStateInfo.values()) {
6.int depth = 0;
7.//根据父子关系计算树枝层次数
8.for (StateInfo i = si; i != null; depth++) {
9.i = i.parentStateInfo;
10.}
11.if (maxDepth < depth) {
12.maxDepth = depth;
13.}
14.}
15.if (mDbg) Log.d(TAG, "completeConstruction: maxDep th=" + maxDepth);
16.//创建mStateStack,mTempStateStack状态栈
17.mStateStack = new StateInfo[maxDepth];
18.mTempStateStack = new StateInfo[maxDepth];
19.//设置状态堆栈
20.setupInitialStateStack();
21./** Sending SM_INIT_CMD message to invoke enter m ethods asynchronously */
22.sendMessageAtFrontOfQueue(obtainMessage(SM_INI T_CMD, mSmHandlerObj));
23.if (mDbg) Log.d(TAG, "completeConstruction: X");
24.}
计算状态树的最大深度方法:
1.遍历状态树中的所有节点;
2.以每一个节点为起始点,根据节点父子关系查找到根节点;
3.累计起始节点到根节点之间的节点个数;查找最大的节点个数。

根据查找到的树的最大节点个数来创建两个状态堆栈,并调用函数setupInitialStateStack来填充该堆栈
[java] view plaincopy
1.private final void setupInitialStateStack() {
2.//在mStateInfo中取得初始状态mInitialState对应的
StateInfo
3.StateInfo curStateInfo = mStateInfo.get(mInitialState);
4.//从初始状态mInitialState开始根据父子关系填充mTempStateStack堆栈
5.for (mTempStateStackCount = 0; curStateInfo != null; mT empStateStackCount++) {
6.mTempStateStack[mTempStateStackCount] = curStateInfo ;
7.curStateInfo = curStateInfo.parentStateInfo;
8.}
9.// Empty the StateStack
10.mStateStackTopIndex = -1;
11.//将mTempStateStack中的状态按反序方式移动到mStateStack栈中
12.moveTempStateStackT oStateStack();
13.}
从上图可以看出,当初始状态为S4时,保存到mTempStateStack的节点为:
mTempStateStack={S4,S1,S0}
mTempStateStackCount = 3;
mStateStackTopIndex = -1;
然后调用函数moveTempStateStackT oStateStack将节点以反序
方式保存到mStateStack中
[java] view plaincopy
1.private final int moveTempStateStackToStateStack() {
2.//startingIndex= 0
3.int startingIndex = mStateStackTopIndex + 1;
4.int i = mTempStateStackCount - 1;
5.int j = startingIndex;
6.while (i >= 0) {
7.if (mDbg) Log.d(TAG, "moveTempStackT oStateStack: i=" + i + ",j=" + j);
8.mStateStack[j] = mTempStateStack[i];
9.j += 1;
10.i -= 1;
11.}
12.mStateStackTopIndex = j - 1;
13.return startingIndex;
14.}
mStateStack={S0,S1,S4}
mStateStackTopIndex = 2
初始化完状态栈后,SmHandler将向消息循环中发送一个SM_INIT_CMD消息
[java] view plaincopy
1.sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_C MD, mSmHandlerObj))
该消息对应的处理如下:
[java] view plaincopy
1.else if (!mIsConstructionCompleted &&(mMsg.what == S M_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
2.mIsConstructionCompleted = true;
3.invokeEnterMethods(0);
4.}
5.performTransitions();
消息处理过程首先调用invokeEnterMethods函数将mStateStack栈中的所有状态设置为激活状态,同时调用每一个状态的enter()函数
[java] view plaincopy
1.private final void invokeEnterMethods(int stateStackEnte ringIndex) {
2.for (int i = stateStackEnteringIndex; i <= mStateStackT opI ndex; i++) {
3.if (mDbg) Log.d(TAG, "invokeEnterMethods: " + mStateSt ack[i].state.getName());
4.mStateStack[i].state.enter();
5.mStateStack[i].active = true;
6.}
7.}
最后调用performTransitions函数来切换状态,同时设置mIsConstructionCompleted为true,表示状态机已经启动完成,SmHandler在以后的消息处理过程中就不在重新启动状态机了。

状态切换
SmHandler在处理每个消息时都会调用performTransitions来检查状态切换
[java] view plaincopy
1.private synchronized void performTransitions() {
2.while (mDestState != null){
3.//当前状态切换了存在于mStateStack中的State需要改变
4.//仍然按照链式父子关系来存储
5.//先从当前状态S3找到最近的被激活的parent状态S0
6.//未被激活的全部保存起来(S3,S1) 返回S0
7.StateInfo commonStateInfo = setupT empStateStackWithS tatesToEnter(destState);
8.//将mStateStack中不属于当前状态(S3),
9.//关系链上的State(S5,S2)退出(执行exit方法)
10.invokeExitMethods(commonStateInfo);
11.//将S3关系链加入到栈中(S3,S1)
12.int stateStackEnteringIndex = moveTempStateStackT o StateStack();
13.//将新加入到mStateStack中未被激活的State激活(S3,S1)
14.invokeEnterMethods(stateStackEnteringIndex);
15.//将延迟的消息移动到消息队列的前面,以便快速得到处理
16.moveDeferredMessageAtFrontOfQueue();
17.}
18.}
首先介绍一下状态切换的思路:
以上图中,初始状态为S4,现在目标状态mDestState被设置为S7。

前面介绍了保存在mStateStack数组中的节点为:
mStateStack={S0,S1,S4}
mStateStackTopIndex = 2
这是以初始状态节点为起点遍历节点树得到的节点链表。

现在要切换到S7状态节点,则以S7为起始节点,同样遍历状态节点树,查找未激活的所有节点,并保存到mTempStateStack数组中mTempStateStack={S7,S2,S0}
mTempStateStackCount = 3
接着调用mStateStack中除S0节点外的其他所有节点的exit函数,并且将每个状态节点设置为未激活状态,因此S4,S1被设置为未激活状态;将切换后的状态节点链表mTempStateStack移动到
mStateStack,
mStateStack={S0,S2,S7}
mStateStackTopIndex = 2
并调用节点S2,S7的enter函数,同时设置为激活状态。

理解了整个状态切换过程后,就能更好地理解代码,首先根据目标状态建立状态节点链路表
[java] view plaincopy
1.private final StateInfo setupTempStateStackWithStatesT o Enter(State destState) {
2.mTempStateStackCount = 0;
3.StateInfo curStateInfo = mStateInfo.get(destState);
4.do {
5.mTempStateStack[mTempStateStackCount++] = curStateI nfo;
6.if (curStateInfo != null) {
7.curStateInfo = curStateInfo.parentStateInfo;
8.}
9.} while ((curStateInfo != null) && !curStateInfo.active);
10.return curStateInfo;
11.}
然后弹出mStateStack中保存的原始状态
[java] view plaincopy
1.private final void invokeExitMethods(StateInfo common StateInfo) {
2.while ((mStateStackTopIndex >= 0) &&
3.(mStateStack[mStateStackT opIndex] != commonStateInfo) ) {
4.State curState = mStateStack[mStateStackT opIndex].state;
5.if (mDbg) Log.d(TAG, "invokeExitMethods: " + curState.ge
tName());
6.curState.exit();
7.mStateStack[mStateStackT opIndex].active = false;
8.mStateStackTopIndex -= 1;
9.}
10.}
将新建立的状态节点链表保存到mStateStack栈中
[java] view plaincopy
1.private final int moveTempStateStackToStateStack() {
2.//startingIndex= 0
3.int startingIndex = mStateStackTopIndex + 1;
4.int i = mTempStateStackCount - 1;
5.int j = startingIndex;
6.while (i >= 0) {
7.if (mDbg) Log.d(TAG, "moveTempStackT oStateStack: i=" + i + ",j=" + j);
8.mStateStack[j] = mTempStateStack[i];
9.j += 1;
10.i -= 1;
11.}
12.mStateStackTopIndex = j - 1;
13.return startingIndex;
14.}
初始化入栈的所有新状态,并设置为激活状态
[java] view plaincopy
1.private final void invokeEnterMethods(int stateStackEnte ringIndex) {
2.for (int i = stateStackEnteringIndex; i <= mStateStackT opI ndex; i++) {
3.if (mDbg) Log.d(TAG, "invokeEnterMethods: " + mStateSt
ack[i].state.getName());
4.mStateStack[i].state.enter();
5.mStateStack[i].active = true;
6.}
7.}
如何设置目标状态呢?StateMachine提供了transitionTo函数来切换状态
[java] view plaincopy
1.protected final void transitionTo(IState destState) {
2.mSmHandler.transitionTo(destState);
3.}
该函数直接调用SmHandler的transitionTo函数来实现,SmHandler的transitionTo函数定义如下:
[java] view plaincopy
1.private final void transitionTo(IState destState) {
2.mDestState = (State) destState;
3.if (mDbg) Log.d(TAG, "transitionT o: destState=" + mDestS tate.getName());
4.}
这里只是简单地设置了mDestState变量,并未真正更新状态栈mStateStack,在前面介绍了SmHandler在每次处理消息时都会自动更新一次mStateStack,无论mDestState变量值是否改变。

由此可知目标状态的设置与状态栈的更新是异步的。

消息处理
StateMachine处理的核心就是SmHandler,就是一个Handler,运行在单独线程中。

Handler是用来异步处理派发消息,这里使用Handler管理各个状态,派发消息处理到各个状态中去执行。

StateMachine提供了多个消息发送接口,通过这些接口函数可以将消息发送到SmHandler中。

[java] view plaincopy
1.public final void sendMessage(int what) {
2.// mSmHandler can be null if the state machine has quit.
3.if (mSmHandler == null) return;
4.mSmHandler.sendMessage(obtainMessage(what));
5.}
SmHandler将处理通过SmHandler发送的消息,处理过程如下:[java] view plaincopy
1.public final void handleMessage(Message msg) {
2./** Save the current message */
3.mMsg = msg;
4.if (mIsConstructionCompleted) {
5.//派发当前消息到state中去处理
6.processMsg(msg);
7.} else if (!mIsConstructionCompleted &&
8.(mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSm HandlerObj)) {
9./** Initial one time path. */
10.mIsConstructionCompleted = true;
11.invokeEnterMethods(0);
12.} else {
13.throw new RuntimeException("StateMachine.handleM essage: " +
14."The start method not called, received msg: " + msg);
15.}
16.//消息处理完毕更新mStateStack
17.performTransitions();
18.}
变量mIsConstructionCompleted在状态机启动完成后被设置为
true,因此这里将调用processMsg函数来完成消息处理。

[java] view plaincopy
1.private final void processMsg(Message msg) {
2.StateInfo curStateInfo = mStateStack[mStateStackT opInde x];
3.//如果当前状态未处理该消息
4.while (!curStateInfo.state.processMessage(msg)) {
5.//将消息传给当前状态的父节点处理
6.curStateInfo = curStateInfo.parentStateInfo;
7.if (curStateInfo == null) {
8.//当前状态无父几点,则丢弃该消息
9.mSm.unhandledMessage(msg);
10.if (isQuit(msg)) {
11.transitionTo(mQuittingState);
12.}
13.break;
14.}
15.}
16.//记录处理过的消息
17.if (mSm.recordProcessedMessage(msg)) {
18.if (curStateInfo != null) {
19.State orgState = mStateStack[mStateStackT opIndex].st ate;
20.mProcessedMessages.add(msg, mSm.getMessageInfo (msg), curStateInfo.state,orgState);
21.} else {
22.mProcessedMessages.add(msg, mSm.getMessageInfo (msg), null, null);
23.}
24.}
25.}
消息处理过程是从mStateStack栈顶派发到栈底,直到该消息被处理!
更多0
•Android APN配置
•Android 匿名共享内存驱动源码分析。

相关文档
最新文档