分类:android应用分析 1749人阅读 评论(1) 收藏 举报 android状态机详解 statemachine android 状态state

先说两句题外话,很感谢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 plain copy print ?
  1. if(mIsConstructionCompleted){
  2. /**Normalpath*/
  3. processMsg(msg);//第一个消息处理
  4. }elseif(!mIsConstructionCompleted&&
  5. (mMsg.what==SM_INIT_CMD)&&(mMsg.obj==mSmHandlerObj)){
  6. /**Initialonetimepath.*/
  7. mIsConstructionCompleted=true;
  8. invokeEnterMethods(0);//第二个状态的初始化
  9. }else{
  10. thrownewRuntimeException("StateMachine.handleMessage:"+
  11. "Thestartmethodnotcalled,receivedmsg:"+msg);
  12. }
  13. performTransitions();//第三个执行状态转移

首先去看下processMsg方法

[java] view plain copy print ?
  1. while(!curStateInfo.state.processMessage(msg)){
  2. /**
  3. *Notprocessed
  4. */
  5. curStateInfo=curStateInfo.parentStateInfo;
  6. if(curStateInfo==null){
  7. /**
  8. *Noparentsleftsoit'snothandled
  9. */
  10. mSm.unhandledMessage(msg);
  11. break;
  12. }
  13. if(mDbg){
  14. Log.d(TAG,"processMsg:"+curStateInfo.state.getName());
  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 plain copy print ?
  1. addState(mDisconnected);
  2. addState(mPending);
  3. addState(mConnected);
  4. addState(mAudioOn);
  5. setInitialState(mDisconnected);

以上两块代码,第一块是建立本状态机的整个框架,就相当于建立整个状态树,第二个是设置初始化状态。再看看HeadsetStateMachine中的静态代码块

[java] view plain copy print ?
  1. staticHeadsetStateMachinemake(HeadsetServicecontext){
  2. Log.d(TAG,"make");
  3. HeadsetStateMachinehssm=newHeadsetStateMachine(context);
  4. hssm.start();
  5. returnhssm;
  6. }

以上两块代码,第一块是建立本状态机的整个框架,就相当于建立整个状态树,第二个是设置初始化状态。再看看HeadsetStateMachine中的静态代码块
这里面有一个start()方法,从这个方法开始,状态机开始运作,包括分配内存,根据初始化状态设置初始化状态路径,这一点主要在setupInitialStateStack方法中执行,依次执行状态路径上每个状态的enter方法,这个使用了消息机制sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));,最终就在本段开头的invokeEnterMethods方法中执行。
到这里状态机主要内容基本讲解完毕,貌似绝大多数都需要记忆,记住了感觉就理解到了。:-D 有点像本文开头说的。初一看感觉没有什么,但是如果想象下你有一个这样的需求,耳机和手机的状态一直在切换,你会采用什么方式去做,在考虑了很多之后会感觉状态机真的是一个很厉害的东西。:-D 接下来附上android源码中的demo,为了方便理解,笔者将输出增加了一些空行,多余的空行不是demo打印的。

[java] view plain copy print ?
  1. classHsm1extendsStateMachine{
  2. privatestaticfinalStringTAG="hsm1";
  3. publicstaticfinalintCMD_1=1;
  4. publicstaticfinalintCMD_2=2;
  5. publicstaticfinalintCMD_3=3;
  6. publicstaticfinalintCMD_4=4;
  7. publicstaticfinalintCMD_5=5;
  8. publicstaticHsm1makeHsm1(){
  9. Log.d(TAG,"makeHsm1E");
  10. Hsm1sm=newHsm1("hsm1");
  11. sm.start();
  12. Log.d(TAG,"makeHsm1X");
  13. returnsm;
  14. }
  15. Hsm1(Stringname){
  16. super(name);
  17. Log.d(TAG,"ctorE");
  18. //Addstates,useindentationtoshowhierarchy
  19. addState(mP1);
  20. addState(mS1,mP1);
  21. addState(mS2,mP1);
  22. addState(mP2);
  23. //Settheinitialstate
  24. setInitialState(mS1);
  25. Log.d(TAG,"ctorX");
  26. }
  27. classP1extendsState{
  28. @Overridepublicvoidenter(){
  29. Log.d(TAG,"mP1.enter");
  30. }
  31. @OverridepublicbooleanprocessMessage(Messagemessage){
  32. booleanretVal;
  33. Log.d(TAG,"mP1.processMessagewhat="+message.what);
  34. switch(message.what){
  35. caseCMD_2:
  36. //CMD_2willarriveinmS2beforeCMD_3
  37. sendMessage(obtainMessage(CMD_3));
  38. deferMessage(message);
  39. transitionTo(mS2);
  40. retVal=HANDLED;
  41. break;
  42. default:
  43. //Anymessagewedon'tunderstandinthisstateinvokesunhandledMessage
  44. retVal=NOT_HANDLED;
  45. break;
  46. }
  47. returnretVal;
  48. }
  49. @Overridepublicvoidexit(){
  50. Log.d(TAG,"mP1.exit");
  51. }
  52. }
  53. classS1extendsState{
  54. @Overridepublicvoidenter(){
  55. Log.d(TAG,"mS1.enter");
  56. }
  57. @OverridepublicbooleanprocessMessage(Messagemessage){
  58. Log.d(TAG,"S1.processMessagewhat="+message.what);
  59. if(message.what==CMD_1){
  60. //Transitiontoourselftoshowthatenter/exitiscalled
  61. transitionTo(mS1);
  62. returnHANDLED;
  63. }else{
  64. //Letparentprocessallothermessages
  65. returnNOT_HANDLED;
  66. }
  67. }
  68. @Overridepublicvoidexit(){
  69. Log.d(TAG,"mS1.exit");
  70. }
  71. }
  72. classS2extendsState{
  73. @Overridepublicvoidenter(){
  74. Log.d(TAG,"mS2.enter");
  75. }
  76. @OverridepublicbooleanprocessMessage(Messagemessage){
  77. booleanretVal;
  78. Log.d(TAG,"mS2.processMessagewhat="+message.what);
  79. switch(message.what){
  80. case(CMD_2):
  81. sendMessage(obtainMessage(CMD_4));
  82. retVal=HANDLED;
  83. break;
  84. case(CMD_3):
  85. deferMessage(message);
  86. transitionTo(mP2);
  87. retVal=HANDLED;
  88. break;
  89. default:
  90. retVal=NOT_HANDLED;
  91. break;
  92. }
  93. returnretVal;
  94. }
  95. @Overridepublicvoidexit(){
  96. Log.d(TAG,"mS2.exit");
  97. }
  98. }
  99. classP2extendsState{
  100. @Overridepublicvoidenter(){
  101. Log.d(TAG,"mP2.enter");
  102. sendMessage(obtainMessage(CMD_5));
  103. }
  104. @OverridepublicbooleanprocessMessage(Messagemessage){
  105. Log.d(TAG,"P2.processMessagewhat="+message.what);
  106. switch(message.what){
  107. case(CMD_3):
  108. break;
  109. case(CMD_4):
  110. break;
  111. case(CMD_5):
  112. transitionToHaltingState();
  113. break;
  114. }
  115. returnHANDLED;
  116. }
  117. @Overridepublicvoidexit(){
  118. Log.d(TAG,"mP2.exit");
  119. }
  120. }
  121. @Override
  122. voidonHalting(){
  123. Log.d(TAG,"halting");
  124. synchronized(this){
  125. this.notifyAll();
  126. }
  127. }
  128. P1mP1=newP1();
  129. S1mS1=newS1();
  130. S2mS2=newS2();
  131. P2mP2=newP2();
  132. }
  133. </code>
  134. *<p>IfthisisexecutedbysendingtwomessagesCMD_1andCMD_2
  135. *(Notethesynchronizeisonlyneededbecauseweusehsm.wait())</p>
  136. <code>
  137. Hsm1hsm=makeHsm1();
  138. synchronize(hsm){
  139. hsm.sendMessage(obtainMessage(hsm.CMD_1));
  140. hsm.sendMessage(obtainMessage(hsm.CMD_2));
  141. try{
  142. //waitforthemessagestobehandled
  143. hsm.wait();
  144. }catch(InterruptedExceptione){
  145. Log.e(TAG,"exceptionwhilewaiting"+e.getMessage());
  146. }
  147. }
  148. 输出如下:
  149. D/hsm1(1999):makeHsm1E
  150. D/hsm1(1999):ctorE
  151. D/hsm1(1999):ctorX
  152. D/hsm1(1999):mP1.enter
  153. D/hsm1(1999):mS1.enter
  154. D/hsm1(1999):makeHsm1X
  155. D/hsm1(1999):mS1.processMessagewhat=1
  156. D/hsm1(1999):mS1.exit
  157. D/hsm1(1999):mS1.enter
  158. D/hsm1(1999):mS1.processMessagewhat=2
  159. D/hsm1(1999):mP1.processMessagewhat=2
  160. D/hsm1(1999):mS1.exit
  161. D/hsm1(1999):mS2.enter
  162. D/hsm1(1999):mS2.processMessagewhat=2
  163. D/hsm1(1999):mS2.processMessagewhat=3
  164. D/hsm1(1999):mS2.exit
  165. D/hsm1(1999):mP1.exit
  166. D/hsm1(1999):mP2.enter
  167. D/hsm1(1999):mP2.processMessagewhat=3
  168. D/hsm1(1999):mP2.processMessagewhat=4
  169. D/hsm1(1999):mP2.processMessagewhat=5
  170. D/hsm1(1999):mP2.exit
  171. D/hsm1(1999):halting


Android状态机源码分析

分类:【Android 系统分析】 470人阅读 评论(0) 收藏 举报 Android StateMachine 状态机 Handler state

目录(?)[+]

在Android系统中,经常使用状态机来处理不同状态下的行为动作。状态机是将对象的状态与行为封装在一起;可以解决庞大的分支语句带来程序阅读性差和不便于进行扩展问题,使整个结构变得更加清晰明了,降低程序管理的复杂性提高灵活度。Android系统的StateMachine机制是一个State模式的应用,StateMachine是一个分层处理消息的状态机,并且是能够有分层排列状态。


构造状态机

StateMachine的构造函数都是protected类型,不能实例化,都是由其子类进行初始化操作。StateMachine有两个重载的构造函数,一个是通过指定消息循环对象来构造状态机

[java] view plain copy
  1. protectedStateMachine(Stringname,Looperlooper){
  2. initStateMachine(name,looper);
  3. }

另一个则是直接启动一个消息循环线程来构造一个状态机

[java] view plain copy
  1. protectedStateMachine(Stringname){
  2. //启动一个消息循环线程
  3. mSmThread=newHandlerThread(name);
  4. mSmThread.start();
  5. Looperlooper=mSmThread.getLooper();
  6. initStateMachine(name,looper);
  7. }
这两种构造函数都会通过initStateMachine函数来初始化该状态机

[java] view plain copy
  1. privatevoidinitStateMachine(Stringname,Looperlooper){
  2. mName=name;
  3. mSmHandler=newSmHandler(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 plain copy
  1. protectedfinalvoidaddState(Statestate,Stateparent){
  2. mSmHandler.addState(state,parent);
  3. }
直接通过mSmHandler来完成状态添加过程 [java] view plain copy
  1. privatefinalStateInfoaddState(Statestate,Stateparent){
  2. if(mDbg){
  3. Log.d(TAG,"addStateInternal:Estate="+state.getName()
  4. +",parent="+((parent==null)?"":parent.getName()));
  5. }
  6. StateInfoparentStateInfo=null;
  7. if(parent!=null){
  8. parentStateInfo=mStateInfo.get(parent);
  9. if(parentStateInfo==null){
  10. //Recursivelyaddourparentasit'snotbeenaddedyet.
  11. parentStateInfo=addState(parent,null);
  12. }
  13. }
  14. StateInfostateInfo=mStateInfo.get(state);
  15. if(stateInfo==null){
  16. stateInfo=newStateInfo();
  17. mStateInfo.put(state,stateInfo);
  18. }
  19. //Validatethatwearen'taddingthesamestateintwodifferenthierarchies.
  20. if((stateInfo.parentStateInfo!=null)&&
  21. (stateInfo.parentStateInfo!=parentStateInfo)){
  22. thrownewRuntimeException("statealreadyadded");
  23. }
  24. stateInfo.state=state;
  25. stateInfo.parentStateInfo=parentStateInfo;
  26. stateInfo.active=false;
  27. if(mDbg)Log.d(TAG,"addStateInternal:XstateInfo:"+stateInfo);
  28. returnstateInfo;
  29. }
状态添加过程其实就是为每个State创建相应的StateInfo对象,通过该对象来建立各个状态之间的关系,并且一个State-StateInfo键值对的方式保存到mStateInfo Hash表中。StateInfo就是包装State组成一个Node,建立State的父子关系;mStateInfo =new HashMap<State, StateInfo>();用来保存State Machine中的所有State,可以按照树形层次结构组织状态机中的所有状态 接下来介绍构造以下树形状态机的过程: [java] view plain copy
  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 plain copy
  1. privatefinalStateInfoaddState(S0,null)
  2. {
  3. StateInfoparentStateInfo=null;
  4. //状态S0还未加入状态机中
  5. StateInfostateInfo=mStateInfo.get(state);
  6.   if(stateInfo==null){
  7.     //创建State详细信息对象
  8.     stateInfo=newStateInfo();
  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.   returnstateInfo;
  18. }
2.添加树枝节点状态过程 对于子节点S1状态的加入过程如下sm.addState(S1,S0):
[java] view plain copy
  1. privatefinalStateInfoaddState(Statestate,Stateparent)
  2. {
  3.   StateInfoparentStateInfo=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.   StateInfostateInfo=mStateInfo.get(state);
  16.   if(stateInfo==null){
  17.     //创建State详细信息对象
  18.     stateInfo=newStateInfo();
  19. //将S1加入状态机中
  20.     mStateInfo.put(state,stateInfo);
  21.   }
  22.   //S1的状态信息刚创建,还没有为其设置父节点,因此其父节点为空
  23.   if((stateInfo.parentStateInfo!=null)&&
  24.     (stateInfo.parentStateInfo!=parentStateInfo)){
  25.       thrownewRuntimeException("statealreadyadded");
  26.   }
  27.   //设置状态S1的状态信息
  28.  stateInfo.state=state;
  29. //S1的父节点为S0
  30.  stateInfo.parentStateInfo=parentStateInfo;
  31.   stateInfo.active=false;
  32.   returnstateInfo;
  33. }
对于其他树枝节点的添加过程类似,这里不在介绍,最后保存在mStateInfo表中的所有状态之间就建立了以下树形关系:

启动状态机

当向状态机中添加完所有状态时,通过函数start函数来启动状态机 [java] view plain copy
  1. publicvoidstart(){
  2. //mSmHandlercanbenullifthestatemachinehasquit.
  3. if(mSmHandler==null)return;
  4. /**Sendthecompleteconstructionmessage*/
  5. mSmHandler.completeConstruction();
  6. }
调用mSmHandler的completeConstruction函数来完成状态机的构造完成处理 [java] view plain copy
  1. privatefinalvoidcompleteConstruction(){
  2. if(mDbg)Log.d(TAG,"completeConstruction:E");
  3. //查找状态树的深度
  4. intmaxDepth=0;
  5. for(StateInfosi:mStateInfo.values()){
  6. intdepth=0;
  7. //根据父子关系计算树枝层次数
  8. for(StateInfoi=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:maxDepth="+maxDepth);
  16. //创建mStateStack,mTempStateStack状态栈
  17. mStateStack=newStateInfo[maxDepth];
  18. mTempStateStack=newStateInfo[maxDepth];
  19. //设置状态堆栈
  20. setupInitialStateStack();
  21. /**SendingSM_INIT_CMDmessagetoinvokeentermethodsasynchronously*/
  22. sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD,mSmHandlerObj));
  23. if(mDbg)Log.d(TAG,"completeConstruction:X");
  24. }
计算状态树的最大深度方法: 1.遍历状态树中的所有节点; 2.以每一个节点为起始点,根据节点父子关系查找到根节点; 3.累计起始节点到根节点之间的节点个数;查找最大的节点个数。 根据查找到的树的最大节点个数来创建两个状态堆栈,并调用函数setupInitialStateStack来填充该堆栈 [java] view plain copy
  1. privatefinalvoidsetupInitialStateStack(){
  2. //在mStateInfo中取得初始状态mInitialState对应的StateInfo
  3. StateInfocurStateInfo=mStateInfo.get(mInitialState);
  4. //从初始状态mInitialState开始根据父子关系填充mTempStateStack堆栈
  5. for(mTempStateStackCount=0;curStateInfo!=null;mTempStateStackCount++){
  6. mTempStateStack[mTempStateStackCount]=curStateInfo;
  7. curStateInfo=curStateInfo.parentStateInfo;
  8. }
  9. //EmptytheStateStack
  10. mStateStackTopIndex=-1;
  11. //将mTempStateStack中的状态按反序方式移动到mStateStack栈中
  12. moveTempStateStackToStateStack();
  13. }
从上图可以看出,当初始状态为S4时,保存到mTempStateStack的节点为: mTempStateStack={S4,S1,S0}
mTempStateStackCount = 3; mStateStackTopIndex = -1; 然后调用函数moveTempStateStackToStateStack将节点以反序方式保存到mStateStack中 [java] view plain copy
  1. privatefinalintmoveTempStateStackToStateStack(){
  2. //startingIndex=0
  3. intstartingIndex=mStateStackTopIndex+1;
  4. inti=mTempStateStackCount-1;
  5. intj=startingIndex;
  6. while(i>=0){
  7. if(mDbg)Log.d(TAG,"moveTempStackToStateStack:i="+i+",j="+j);
  8. mStateStack[j]=mTempStateStack[i];
  9. j+=1;
  10. i-=1;
  11. }
  12. mStateStackTopIndex=j-1;
  13. returnstartingIndex;
  14. }
mStateStack={S0,S1,S4} mStateStackTopIndex = 2
初始化完状态栈后,SmHandler将向消息循环中发送一个SM_INIT_CMD消息 [java] view plain copy
  1. sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD,mSmHandlerObj))
该消息对应的处理如下: [java] view plain copy
  1. elseif(!mIsConstructionCompleted&&(mMsg.what==SM_INIT_CMD)&&(mMsg.obj==mSmHandlerObj)){
  2. mIsConstructionCompleted=true;
  3. invokeEnterMethods(0);
  4. }
  5. performTransitions();
消息处理过程首先调用invokeEnterMethods函数将mStateStack栈中的所有状态设置为激活状态,同时调用每一个状态的enter()函数 [java] view plain copy
  1. privatefinalvoidinvokeEnterMethods(intstateStackEnteringIndex){
  2. for(inti=stateStackEnteringIndex;i<=mStateStackTopIndex;i++){
  3. if(mDbg)Log.d(TAG,"invokeEnterMethods:"+mStateStack[i].state.getName());
  4. mStateStack[i].state.enter();
  5. mStateStack[i].active=true;
  6. }
  7. }
最后调用performTransitions函数来切换状态,同时设置mIsConstructionCompleted为true,表示状态机已经启动完成,SmHandler在以后的消息处理过程中就不在重新启动状态机了。

状态切换


SmHandler在处理每个消息时都会调用performTransitions来检查状态切换 [java] view plain copy
  1. privatesynchronizedvoidperformTransitions(){
  2.   while(mDestState!=null){
  3.     //当前状态切换了存在于mStateStack中的State需要改变
  4.     //仍然按照链式父子关系来存储
  5.     //先从当前状态S3找到最近的被激活的parent状态S0
  6.     //未被激活的全部保存起来(S3,S1)返回S0
  7.     StateInfocommonStateInfo=setupTempStateStackWithStatesToEnter(destState);
  8.     //将mStateStack中不属于当前状态(S3),
  9.     //关系链上的State(S5,S2)退出(执行exit方法)
  10.     invokeExitMethods(commonStateInfo);
  11.     //将S3关系链加入到栈中(S3,S1)
  12.     intstateStackEnteringIndex=moveTempStateStackToStateStack();
  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 plain copy
  1. privatefinalStateInfosetupTempStateStackWithStatesToEnter(StatedestState){
  2. mTempStateStackCount=0;
  3. StateInfocurStateInfo=mStateInfo.get(destState);
  4. do{
  5. mTempStateStack[mTempStateStackCount++]=curStateInfo;
  6. if(curStateInfo!=null){
  7. curStateInfo=curStateInfo.parentStateInfo;
  8. }
  9. }while((curStateInfo!=null)&&!curStateInfo.active);
  10. returncurStateInfo;
  11. }
然后弹出mStateStack中保存的原始状态 [java] view plain copy
  1. privatefinalvoidinvokeExitMethods(StateInfocommonStateInfo){
  2. while((mStateStackTopIndex>=0)&&
  3. (mStateStack[mStateStackTopIndex]!=commonStateInfo)){
  4. StatecurState=mStateStack[mStateStackTopIndex].state;
  5. if(mDbg)Log.d(TAG,"invokeExitMethods:"+curState.getName());
  6. curState.exit();
  7. mStateStack[mStateStackTopIndex].active=false;
  8. mStateStackTopIndex-=1;
  9. }
  10. }
将新建立的状态节点链表保存到mStateStack栈中 [java] view plain copy
  1. privatefinalintmoveTempStateStackToStateStack(){
  2. //startingIndex=0
  3. intstartingIndex=mStateStackTopIndex+1;
  4. inti=mTempStateStackCount-1;
  5. intj=startingIndex;
  6. while(i>=0){
  7. if(mDbg)Log.d(TAG,"moveTempStackToStateStack:i="+i+",j="+j);
  8. mStateStack[j]=mTempStateStack[i];
  9. j+=1;
  10. i-=1;
  11. }
  12. mStateStackTopIndex=j-1;
  13. returnstartingIndex;
  14. }
初始化入栈的所有新状态,并设置为激活状态 [java] view plain copy
  1. privatefinalvoidinvokeEnterMethods(intstateStackEnteringIndex){
  2. for(inti=stateStackEnteringIndex;i<=mStateStackTopIndex;i++){
  3. if(mDbg)Log.d(TAG,"invokeEnterMethods:"+mStateStack[i].state.getName());
  4. mStateStack[i].state.enter();
  5. mStateStack[i].active=true;
  6. }
  7. }
如何设置目标状态呢?StateMachine提供了transitionTo函数来切换状态 [java] view plain copy
  1. protectedfinalvoidtransitionTo(IStatedestState){
  2. mSmHandler.transitionTo(destState);
  3. }
该函数直接调用SmHandler的transitionTo函数来实现,SmHandler的transitionTo函数定义如下: [java] view plain copy
  1. privatefinalvoidtransitionTo(IStatedestState){
  2. mDestState=(State)destState;
  3. if(mDbg)Log.d(TAG,"transitionTo:destState="+mDestState.getName());
  4. }
这里只是简单地设置了mDestState变量,并未真正更新状态栈 mStateStack,在前面介绍了SmHandler在每次处理消息时都会自动更新一次mStateStack,无论mDestState变量值是否改变。由此可知目标状态的设置与状态栈的更新是异步的。

消息处理


StateMachine处理的核心就是SmHandler,就是一个Handler,运行在单独线程中。Handler是用来异步处理派发消息,这里使用Handler管理各个状态,派发消息处理到各个状态中去执行。StateMachine提供了多个消息发送接口,通过这些接口函数可以将消息发送到SmHandler中。 [java] view plain copy
  1. publicfinalvoidsendMessage(intwhat){
  2. //mSmHandlercanbenullifthestatemachinehasquit.
  3. if(mSmHandler==null)return;
  4. mSmHandler.sendMessage(obtainMessage(what));
  5. }
SmHandler将处理通过SmHandler发送的消息,处理过程如下: [java] view plain copy
  1. publicfinalvoidhandleMessage(Messagemsg){
  2. /**Savethecurrentmessage*/
  3. mMsg=msg;
  4. if(mIsConstructionCompleted){
  5. //派发当前消息到state中去处理
  6. processMsg(msg);
  7. }elseif(!mIsConstructionCompleted&&
  8. (mMsg.what==SM_INIT_CMD)&&(mMsg.obj==mSmHandlerObj)){
  9. /**Initialonetimepath.*/
  10. mIsConstructionCompleted=true;
  11. invokeEnterMethods(0);
  12. }else{
  13. thrownewRuntimeException("StateMachine.handleMessage:"+
  14. "Thestartmethodnotcalled,receivedmsg:"+msg);
  15. }
  16. //消息处理完毕更新mStateStack
  17. performTransitions();
  18. }
变量mIsConstructionCompleted在状态机启动完成后被设置为true,因此这里将调用processMsg函数来完成消息处理。 [java] view plain copy
  1. privatefinalvoidprocessMsg(Messagemsg){
  2. StateInfocurStateInfo=mStateStack[mStateStackTopIndex];
  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. StateorgState=mStateStack[mStateStackTopIndex].state;
  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栈顶派发到栈底,直到该消息被处理!

更多 1
0

更多相关文章

  1. Android(安卓)深入解析用户界面(一)
  2. android实现推送实践
  3. 【Android】Android消息处理机制
  4. Android进入suspend状态(goToSleep)
  5. Android应用程序的生命周期
  6. Android中的多线程之handler
  7. Android之——史上最简单自定义开关按钮的实现
  8. Android(安卓)Power Management
  9. Android(安卓)Handler 异步消息处理机制 《第一行代码》

随机推荐

  1. Android之Handler详解(四)
  2. Android LayoutInflater
  3. eclipse导入的Android项目没有android.ja
  4. Android面试系列文章2018之Android部分Ha
  5. Android SearchView 搜索框
  6. Android打印相关
  7. android:weight的使用
  8. Android(安卓)Gradle 插件中文指南
  9. Android设置显示文本
  10. Android 高级进阶之路 《总结篇》