android状态机statemachine详解


分类: 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.     /** Normal path */  
  3.     processMsg(msg);//第一个消息处理  
  4. else if (!mIsConstructionCompleted &&  
  5.         (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {  
  6.     /** Initial one time path. */  
  7.     mIsConstructionCompleted = true;  
  8.     invokeEnterMethods(0);//第二个状态的初始化  
  9. else {  
  10.     throw new RuntimeException("StateMachine.handleMessage: " +  
  11.                 "The start method not called, received msg: " + msg);  
  12. }  
  13. performTransitions();//第三个执行状态转移  

首先去看下processMsg方法

[java]  view plain copy print ?
  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.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.   
  6. setInitialState(mDisconnected);  

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

[java]  view plain copy print ?
  1. static HeadsetStateMachine make(HeadsetService context) {  
  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 plain copy print ?
  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 invokes 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.what);  
  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;  
  100.             }  
  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.what);  
  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");  
  128.         }  
  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 CMD_1 and CMD_2  
  146.  * (Note the synchronize is only needed because we use 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 系统分析】   470人阅读  评论(0)  收藏  举报
Android StateMachine 状态机 Handler state

目录(?)[+]

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

android状态机statemachine详解_第1张图片

构造状态机

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

[java]  view plain copy
  1. protected StateMachine(String name, Looper looper) {  
  2.     initStateMachine(name, looper);  
  3. }  

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

[java]  view plain copy
  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 plain copy
  1. private void initStateMachine(String name, Looper looper) {  
  2.     mName = name;  
  3.     mSmHandler = new SmHandler(looper, this);  
  4. }  
初始化过程比较简单,就是将状态机名称保存到成员变量mName中,同时创建SmHandler对象,SmHandler是一个Handle对象,用于派发消息。
android状态机statemachine详解_第2张图片

状态机中的每个状态使用State来封装,对于每个状态的信息又采用StateInfo来描述

android状态机statemachine详解_第3张图片

StateMachine三个内部类:
1.ProcessedMessageInfo:保存已处理消息的信息;
2.ProcessedMessages:存储StateMachine最近处理的一些消息,需要保存最近处理的消息条数默认20,可以用户自己设定最大数目;
3.SmHandle是消息处理派发和状态控制切换的核心,运行在单独的线程上;

SmHandle成员变量定义:

android状态机statemachine详解_第4张图片

android状态机statemachine详解_第5张图片

ProcessedMessages用于保存已处理过的消息,mStateStack和mTempStateStack是一个数组栈,用于保存状态机中的链式状态关系。

android状态机statemachine详解_第6张图片

mStateInfo定义为一个Hash链表,用于保存添加的所有状态。mInitialState保存初始状态,mDestState保存切换的目的状态。

建立树形层次结构状态机

在构造完一个状态机前需要向状态机中添加各种状态,StateMachine提供了addState函数来添加状态
[java]  view plain copy
  1. protected final void addState(State state, State parent) {  
  2.     mSmHandler.addState(state, parent);  
  3. }  
直接通过mSmHandler来完成状态添加过程
[java]  view plain copy
  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 yet.  
  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 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. 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 plain copy
  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表中的所有状态之间就建立了以下树形关系:
android状态机statemachine详解_第7张图片

启动状态机

当向状态机中添加完所有状态时,通过函数start函数来启动状态机
[java]  view plain copy
  1. public void start() {  
  2.     // mSmHandler can be null if the state machine has quit.  
  3.     if (mSmHandler == nullreturn;  
  4.     /** Send the complete construction message */  
  5.     mSmHandler.completeConstruction();  
  6. }  
调用mSmHandler的completeConstruction函数来完成状态机的构造完成处理
[java]  view plain copy
  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: maxDepth=" + 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 methods asynchronously */  
  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. private final void setupInitialStateStack() {  
  2.     //在mStateInfo中取得初始状态mInitialState对应的StateInfo  
  3.     StateInfo curStateInfo = mStateInfo.get(mInitialState);  
  4.     //从初始状态mInitialState开始根据父子关系填充mTempStateStack堆栈  
  5.     for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {  
  6.         mTempStateStack[mTempStateStackCount] = curStateInfo;  
  7.         curStateInfo = curStateInfo.parentStateInfo;  
  8.     }  
  9.     // Empty the StateStack  
  10.     mStateStackTopIndex = -1;  
  11.     //将mTempStateStack中的状态按反序方式移动到mStateStack栈中  
  12.     moveTempStateStackToStateStack();  
  13. }  
android状态机statemachine详解_第8张图片
从上图可以看出,当初始状态为S4时,保存到mTempStateStack的节点为:
mTempStateStack={S4,S1,S0}
mTempStateStackCount = 3;
mStateStackTopIndex = -1;
然后调用函数moveTempStateStackToStateStack将节点以反序方式保存到mStateStack中
[java]  view plain copy
  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, "moveTempStackToStateStack: 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 plain copy
  1. sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj))  
该消息对应的处理如下:
[java]  view plain copy
  1. else if (!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. private final void invokeEnterMethods(int stateStackEnteringIndex) {  
  2.     for (int i = 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. private synchronized void performTransitions() {  
  2.   while (mDestState != null){  
  3.     //当前状态切换了 存在于mStateStack中的State需要改变  
  4.     //仍然按照链式父子关系来存储  
  5.     //先从当前状态S3找到 最近的被激活的parent状态S0  
  6.     //未被激活的全部保存起来(S3,S1) 返回S0  
  7.     StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);  
  8.     //将mStateStack中 不属于当前状态(S3),  
  9.     //关系链上的State(S5,S2)退出(执行exit方法)  
  10.     invokeExitMethods(commonStateInfo);  
  11.     //将S3关系链 加入到栈中(S3,S1)  
  12.     int stateStackEnteringIndex = moveTempStateStackToStateStack();  
  13.     //将新加入到mStateStack中 未被激活的State激活(S3,S1)  
  14.     invokeEnterMethods(stateStackEnteringIndex);  
  15.     //将延迟的消息移动到消息队列的前面,以便快速得到处理                 
  16.     moveDeferredMessageAtFrontOfQueue();  
  17.   }  
  18. }  
首先介绍一下状态切换的思路:
android状态机statemachine详解_第9张图片
以上图中,初始状态为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. private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {  
  2.     mTempStateStackCount = 0;  
  3.     StateInfo curStateInfo = 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.     return curStateInfo;  
  11. }  
然后弹出mStateStack中保存的原始状态
[java]  view plain copy
  1. private final void invokeExitMethods(StateInfo commonStateInfo) {  
  2.     while ((mStateStackTopIndex >= 0) &&  
  3.             (mStateStack[mStateStackTopIndex] != commonStateInfo)) {  
  4.         State curState = 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. 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, "moveTempStackToStateStack: 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 plain copy
  1. private final void invokeEnterMethods(int stateStackEnteringIndex) {  
  2.     for (int i = 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. protected final void transitionTo(IState destState) {  
  2.     mSmHandler.transitionTo(destState);  
  3. }  
该函数直接调用SmHandler的transitionTo函数来实现,SmHandler的transitionTo函数定义如下:
[java]  view plain copy
  1. private final void transitionTo(IState destState) {  
  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. public final void sendMessage(int what) {  
  2.     // mSmHandler can be null if the state machine has quit.  
  3.     if (mSmHandler == nullreturn;  
  4.     mSmHandler.sendMessage(obtainMessage(what));  
  5. }  
SmHandler将处理通过SmHandler发送的消息,处理过程如下:
[java]  view plain copy
  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 == mSmHandlerObj)) {  
  9.         /** Initial one time path. */  
  10.         mIsConstructionCompleted = true;  
  11.         invokeEnterMethods(0);  
  12.     } else {  
  13.         throw new RuntimeException("StateMachine.handleMessage: " +  
  14.                     "The start method not called, received msg: " + msg);  
  15.     }  
  16.     //消息处理完毕更新mStateStack  
  17.     performTransitions();  
  18. }  
变量mIsConstructionCompleted在状态机启动完成后被设置为true,因此这里将调用processMsg函数来完成消息处理。
[java]  view plain copy
  1. private final void processMsg(Message msg) {  
  2.     StateInfo curStateInfo = 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.             State orgState = mStateStack[mStateStackTopIndex].state;  
  20.             mProcessedMessages.add(msg, mSm.getMessageInfo(msg), curStateInfo.state,orgState);  
  21.         } else {  
  22.             mProcessedMessages.add(msg, mSm.getMessageInfo(msg), nullnull);  
  23.         }  
  24.     }  
  25. }  
消息处理过程是从mStateStack栈顶派发到栈底,直到该消息被处理!

更多 1
0

你可能感兴趣的:(【Android,android应用分析,系统分析】)