`
寻梦者
  • 浏览: 637568 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Android学习 StateMachine与State模式

 
阅读更多

一 State模式

意图:

  允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。(Objects for States)

  对象内部状态决定行为方式,对象状态改变行为方式改变;但这里为何要强调内部状态,

外部状态改变也同样会影响行为方式的改变,通常外部状态的改变都会反映到内部状态上来。

  Command模式是将命令请求封装成一个为对象,将不同的请求对象参数化以达到同样的调用执行不同的命令;

同样State模式是将对象的状态封装成一个对象,是在不同的状态下同样的调用执行不同的操作。

 

适用性:

  l  一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态来改变行为;

  l  一个操作中含有庞大的多分支条件语句,且这些分支依赖于该对象的状态;

    根据对象的状态改变行为采用了分支语句的实现方式,解决如此多的分支语句,

    将分支语句放入一个类中进行处理,即将对象的状态封装成为一个独立对象;

结构:

      

    

  Context:维护一个State的子类实例,这个实例定义当前的状态;

  State:定义一个接口以封装与Context的一个特定状态相关的行为;

 

Context与State交互:

  l  Context将与状态相关的请求委托给当前的ConcreteState处理;

  l  Context可将自身作为一个参数传递给处理该请求的状态,使得状态对象在必要的时访问Context;

  l  Context是客户使用的主要接口,客户可用对象来配置一个Context,一旦一个Context配置完毕,

    它的客户不再需要直接与状态对象打交道;

  l  Context或ConcreteState子类都可以决定哪个状态是另外哪一个的后继者,以及是在何种条件下进行转换;

 

理解:

  将对象的状态封装成一个对象,特定的状态下具体的操作行为不同,所以将对象的状态封装成一个对象目地,

是为了得到不同的行为方式;封装对象的状态是将对象的状态与行为封装在一起;可以解决庞大的分支语句带来

程序阅读性差和不便于进行扩展问题,使整个结构变得更加清晰明了,降低程序管理的复杂性提高灵活度。

  解决不断根据内部变量属性改变来决定状态,这样对于复杂多状态的维护变得相对麻烦;而要保持对象的状态一致性,

使状态是作为一个对象进行改变,从Context角度来理解,状态的改变就是原子性——设置一个新的状态State对象即可,

是特定状态内部变量属性更改达到一致性。

  谁定义状态转换呢?  一是由Context对象来改变,容易控制对象的状态,保持对象之间完全独立;

二是由State对象来改变设定其下一个状态,很容易控制状态改变,保证状态的连续性;由Context来改变

状态不知道何时该去改变状态比较合适,不知道当前状态处于什么情况,就需要等待当前状态执行结果然后决定;

由State对象本身来改变,则需要Context提供相应的接口来设定当前的状态,并且需要知道下一个状态对象是谁,

至少是一个状态需要被了解,造成各状态对象之间产生了很强的的依赖性,并且在下一个状态不确定的情况下,

在某种情况下才被触发,State对象本身很难去决定下一个对象是谁,你想用State来监听者些情况的触发吗?

NO不可能的;所以需要在具体问题中去权衡选择。

  State对象的创建与销毁,一是当需要State对象创建它切换后销毁它;二是提前创建状态切换不销毁。

对于不同的语言和使用环境,采取的策略会不同。

       下面来看一下Android平台中对于State模式的应用。

 

二 Android中StateMachine机制

  对与State改变切换这种常见常用的处理,只是各个平台框架中处理的方法不同,

这种在处理多状态较为复杂的大部分场景都能见到的策略——状态机(StateMachine)。

在Android中使用的了StateMachine机制就是一个State模式的应用, StateMachine是非常的强大和精妙。

  下面先简单看一下这个StateMachine。

 

StateMachine类作用:

  The state machine defined here is a hierarchical state machine which processes

messages and can have states arranged hierarchically.

  这里的状态机是一个分层处理消息的状态机,并且是能够有分层排列状态。

下面通过这个类层次的结构来:

 

    

这样就构成了State模式:

       Context——StateMachine

       State   ——State

 

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

复制代码
protected StateMachine(String name) 
{   mSmThread
= new HandlerThread(name);   mSmThread.start();   Looper looper = mSmThread.getLooper();   initStateMachine(name, looper); }
复制代码

 

  但是这个类具体是怎么构成上述的层次结构状态和层次消息处理仍然不明确。

  下面继续看一下这个类各个模块的作用。

 

三 Android 中StateMachine模块分析

 

1 State Machine各个模块作用


 

State

复制代码
public class State implements IState
{
  protected State() {}   public void enter() {}   public void exit() {}   public boolean processMessage(Message msg) {}   public String getName() {} }
复制代码

  状态的基类,stateMachine中的状态都是由State派生而来,构造函数protected,不能实例化;

 

StateMachine三个内部类:

ProcessedMessageInfo:  保存已处理消息的信息

复制代码
public static class ProcessedMessageInfo 
{
  private int what; //用户定义消息标识   private State state; //处理当前消息的状态   private State orgState; //消息未被处理前当前的状态 }
复制代码

 

ProcessedMessages

  存储StateMachine最近处理的一些消息,需要保存最近处理的消息条数默认20,可以用户自己设定最大数目。

复制代码
private static class ProcessedMessages {
  private static final int DEFAULT_SIZE = 20;
  private Vector<ProcessedMessageInfo> mMessages = 
    new Vector<ProcessedMessageInfo>();   private int mMaxSize = DEFAULT_SIZE;   private int mOldestIndex = 0;   private int mCount = 0; }
复制代码

 

SmHandler三个内部类:

StateInfo存储当前State,和其parentState,以及是否激活状态;用来构建树形层次结构模型

复制代码
private class StateInfo 
{   
/** the state */   State state;   /** The parent of this state, null if there is no parent */   StateInfo parentStateInfo;   /** True when the state has been entered and on the stack */   boolean active; }
复制代码

 

HaltingStateQuittingState

       都是State的 派生类,用于在状态停止和放弃之后处理的一些事情;都重写了ProcessMessage方法,

在StateMachine没有实际行动仅仅保留用于扩展。

       整个SmHandle是消息处理派发和状态控制切换的核心,运行在单独的线程上。

SmHandle

  数据成员不少,列出其中关键的一些;

复制代码
private static class SmHandler extends Handler 
{   
/** The current message */   private Message mMsg;   /** A list of messages that this state machine has processed */   private ProcessedMessages mProcessedMessages =     new ProcessedMessages();   /** Stack used to manage the current hierarchy of states */   private StateInfo mStateStack[];   /** The map of all of the states in the state machine */   private HashMap<State, StateInfo> mStateInfo =     new HashMap<State, StateInfo>();   /** The initial state that will process the first message */   private State mInitialState;
  
/** The destination state when transitionTo has been invoked */   private State mDestState; }
复制代码

 

SmHandle是构建StateMachine的核心,运行在独立的线程上,有三个功能:

  • 建立树形层次结构存储State;
  • 状态机的StateStack建立和状态切换;
  • 消息处理和派发;

  下面看看是如何完成这三个功能的:

 

2 建立树形层次结构存储State


 

       在构成一个状态机前需要确定当前都多少状态,需要将这些状态集中起来进行管理。

StateMachine提供了这样一个protected类型方法 AddState来将状态

  添加到状态机中。看看这个函数:

  protected final void addState(State state, State parent) {

    mSmHandler.addState(state, parent);

  }实际上还是SmHandle来工作;Go on……

 

复制代码
/****************************************************
* state:    加入state machine的State
* parent:    the parent of state
****************************************************/
private final StateInfo addState(State state, State parent) 
{   StateInfo parentStateInfo
= null;   if (parent != null) {     //获取当前状态parent详细信息 StateInfo     parentStateInfo = mStateInfo.get(parent);     if (parentStateInfo == null) {       //当前状态父状态未加入到StateMachine中,       //递归先加入其Parent State       parentStateInfo = addState(parent, null);     }   }
  //判断当前状态是否加入到 StateMachine层次结构中   StateInfo stateInfo = mStateInfo.get(state);   if (stateInfo == null) {     //创建State详细信息对象,将其加入到StateMachine层次结构中     stateInfo = new StateInfo();     mStateInfo.put(state, stateInfo);   }
  //验证我们没有加入相同的状态,在两个不同层次,否则异常   if ((stateInfo.parentStateInfo != null) &&     (stateInfo.parentStateInfo != parentStateInfo)) {       throw new RuntimeException("state already added");   }
  //完善当前状态信息   stateInfo.state = state;   stateInfo.parentStateInfo = parentStateInfo;   stateInfo.active = false;   return stateInfo; }
复制代码

 

所以StateMachine类中:

    StateInfo就是包装State组成一个Node,建立State的父子关系;

    private HashMap<State, StateInfo> mStateInfo =

      new HashMap<State, StateInfo>();

    就是用来保存State Machine中的State—严格按照树形层次结构组织;

复制代码
例如:  
  SmHandle sm;   sm.addState(S0,
null);   sm.addState(S1,S0);   sm.addState(S2,S0);   sm.addState(S3,S1);   sm.addState(S4,S1);   sm.addState(S5,S2);   sm.addState(S6,S2);   sm.addState(S7,S2);   setInitialState(S4); //设置初始状态
复制代码

  得到的状态树形层次结构如下:

 

                           

    

 

       树形层次结构存储State就是如此完成的:

              存储数据结构:StateInfo以及HashMap<State, StateInfo> mStateInfo

              方法:StateInfo addState(State state, State parent);

 

3 状态机的StateStack建立和状态切换


 

状态机的StateStack建立:

  各状态State加入到StateMachine,各条件初始化OK后,就可以启动状态机了。

  StateMachine提供了方法:

  public void start() 
  {     
/** Send the complete construction message */     mSmHandler.completeConstruction();   }

  

  SmHandle:completeConstruction构建状态机运行模型

复制代码
//Complete the construction of the state machine.
private final void completeConstruction()
{
    //计算State继承层次结构的最大深度以便创建运行时刻State Stack
    int maxDepth = 0;
    for (StateInfo si : mStateInfo.values()) {
           int depth = 0;
           for (StateInfo i = si; i != null; depth++) {
                  i = i.parentStateInfo;
           }
           if (maxDepth < depth) {
                  maxDepth = depth;
           }
    }
//创建State Stack mStateStack = new StateInfo[maxDepth]; mTempStateStack = new StateInfo[maxDepth];
//根据当前mDestState(S5)按照其层次结构沿着其父子关系, //保存此条路径上的StateInfo 存储到State Stack中于是 //例如:S0--S2—S5 存储到mStateStack中 setupInitialStateStack();
//层次结构状态构建完成调用mStateStack中State的enter方法 //使mStateStack中的State 处于active状态 mIsConstructionCompleted = true; mMsg = obtainMessage(SM_INIT_CMD); invokeEnterMethods(0);
//Perform any transitions requested by the enter methods performTransitions(); //待下面分析 }
复制代码

  这里建立State Stack是干什么用的呢?

State Stack里面的元素结构是根据父子关系组成链式结构:S0——S2——S5;S5肯定mDestState,

S2,S0都是其parentState;状态是一种父子关系,那么这两个状态之间存在某种关系;

State对应着行为,这里到底要干什么呢?

    在后面我们可以看到状态行为处理执行都是根据此mStateStack进行的。

 

状态切换:

  StateMachine中提供了方法:  

protected final void transitionTo(IState destState) 
{   mSmHandler.transitionTo(destState); }此方法用来进行状态切换;

  

  SmHandle提供的方法:

private final void transitionTo(IState destState) 
{
  // mDestState保存当前状态 来处理消息;   mDestState = (State) destState; }

  而上面所提到的状态切换:protected final void transitionTo(IState destState);

仅仅是改变了当前状态mDestState,从StateStack建立这里可以看到和这个mDestState相关的还有mStateStack,

如果改变了mDestState,显然这里的mStateStack也是需要进行改变的,使mStateStack仍然是链式层次式结构。

  所以上面这个状态切换其实并不算完整,还需要改变mStateStack;也就是mDestState改变时,

没有同时改变 mStateStack,而是等到消息处理派发状态Handle的时候,当前的状态行为处理完,

切换到下一个状态,即消息处理完毕然后才进行mStateStack的更新。

这个是和状态切换过程相关的:使状态切换和mStateStack的更新独立开来。

 

  状态切换与数据处理过程是这样的:先不管谁来改变State

 

     

      

 

  所以仅仅改变mDestState还不够,还需要改变mStateStack

 

就是这个函数:performTransitions();

       先看看这样一个例子,关系还是上面的S0——S7:

       mStateStack中存储:S0——S2——S5 mDestState为S5 (栈顶)

       现在状态切换为S3,mDestState为S3

       按照父子关系,mStateStack应该存储有:S0——S1——S3

       那么此时S5,S2都要出栈pop from mStateStack

       那我们就是要找到一个点,让S5,S2出栈;S3,S1进栈;

怎么去执行,这就是这个performTransitions干的事情。

  主要代码如下:

复制代码
//Do any transitions
private synchronized void performTransitions() 
{
  while (mDestState != null)   {     //当前状态切换了 存在于mStateStack中的State需要改变     //仍然按照链式父子关系来存储     //先从当前状态S3找到 最近的被激活的parent状态S0     //未被激活的全部保存起来(S3,S1) 返回S0     StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
    //将mStateStack中 不属于当前状态(S3),     //关系链上的State(S5,S2)退出(执行exit方法)     invokeExitMethods(commonStateInfo);
    //将S3关系链 加入到栈中(S3,S1)     int stateStackEnteringIndex = moveTempStateStackToStateStack();
    //将新加入到mStateStack中 未被激活的State激活(S3,S1)     invokeEnterMethods(stateStackEnteringIndex);
    //将延迟的消息移动到消息队列的前面,以便快速得到处理     moveDeferredMessageAtFrontOfQueue();   }
}
复制代码

这样整个状态切换就完成了:

  切换当前状态:mDestState;

  更新状态栈:mStateStack;

  但是当前状态的切换在StateMachine中并没有明确,因为这只是一个状态机负责状态的管理和消息派发;

谁将负责状态的切换还是交由其子类决定;

 

4 消息处理和派发


 

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

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

  状态机准备OK后(状态加入和状态栈构建完成)就可以执行某些行为,接收消息进行处理,派发到当前状态去执行。

看一下SmHandler中handleMessage是如何进行消息处理的。

 

消息接收:

       StateMachine提供了sendMessage等方法将消息加入到消息队列中,当然都是交给SmHandler去处理的。

这就关乎Handler处理消息的机制了;

 

消息派发:    

复制代码
public final void handleMessage(Message msg) 
{
  //处理当前消息到state中去处理   processMsg(msg);
  //消息处理完毕状态切换 更新mStateStack   performTransitions(); }
复制代码

 

 

复制代码
private final void processMsg(Message msg)
{
  //派发消息到state中去处理
  StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
  while (!curStateInfo.state.processMessage(msg))
  {
    //当前状态mDestState 未处理该消息,交给其parent state处理
    curStateInfo = curStateInfo.parentStateInfo;
    if (curStateInfo == null){
      //此消息未被当前层次状态链处理
    }
  }
}
复制代码

 

  到这里看到建立状态栈mStateStack的作用,用来支持进行链式的消息处理;(Chain of Responsibility)

所以这是一个比较强大的状态机!

 

四 Android中StateMachine实例应用

  看一下Android 中WpsStateMachine对于StateMachine的应用。

WpsStateMachine作用:Manages a WPS connection。继承自StateMachine。

       大致的类结构如下:

     

    

构造函数中:

复制代码
WpsStateMachine(Context context, WifiStateMachine wsm, Handler target) 
{
  //初始化基类StateMachine   super(TAG, target.getLooper());
  
//添加状态 建立树形层次结构存储   addState(mDefaultState);   addState(mInactiveState, mDefaultState);   addState(mActiveState, mDefaultState);
  
//设置初始状态   setInitialState(mInactiveState);   //start the state machine   start(); }
复制代码

 

 

  其中具有的状态:DefaultState、ActiveState、InactiveState(都是WpsStateMachine内部类);

这个层次就是DefaultState作为parent,两个children:ActiveState、InactiveState;

  在这些State派生类中的处理函数processMessage中能够看到transitionTo切换状态;

  这里状态切换是由State完成。

 

后记:

这个StateMachine在什么地方使用较多,可以搜索一下代码看到有:

  BluetoothAdapterStateMachine,BluetoothDeviceProfileState,

BluetoothProfileState,DataConnection,DhcpStateMachine,RilMessageDecoder,

WpsStateMachine,WifiStateMachine等等这些地方都用了到了StateMachine,结构都很相似;

  这些地方都属于数据连接相关,其中状态较多,中间连接过程和处理比较复杂。

分享到:
评论

相关推荐

    Android的状态机模式StateMachine与State

    在博文《Android的状态机模式StateMachine与State》中,作者可能会详细介绍如何创建和使用这两种组件。通常,状态机的实现会涉及到以下步骤: 1. **定义状态接口或抽象类**:创建一个State接口或抽象类,其中包含...

    Android-StateMachine-用于有限状态机的KotlinDSL

    在实际使用中,`Android-StateMachine`库通常与Android架构组件(如LiveData、ViewModel)结合使用,以实现响应式编程和生命周期感知。例如,状态机的状态变化可以触发LiveData的更新,进而驱动UI的更新。 在`...

    安卓:StateMachine测试程序

    `StateMachine`的设计模式主要由以下几个核心部分组成: 1. **初始状态(InitialState)**:每个状态机都有一个起始状态,当状态机被创建时,它会进入这个状态。 2. **状态(State)**:状态是状态机中的原子单元...

    Android StateMachine code

    在Android开发中,状态机(StateMachine)是一种设计模式,用于管理对象在不同状态之间的转换。它可以帮助我们构建复杂的逻辑,特别是在处理系统或组件的各种状态变化时。本篇将深入探讨`Android StateMachine`,并...

    Android_-StateMachine.7z

    在Android开发中,状态机(StateMachine)是一种设计模式,用于管理具有多个稳定状态以及在这些状态之间转换的应用程序逻辑。这种模式特别适用于处理复杂且有多种可能状态的应用,例如网络连接状态、用户交互流程...

    StateMachine

    标题中的"StateMachine"一词,通常在IT领域中指的是状态机或者状态机设计模式。状态机是一种模型,用于描述一个系统或对象在其生命周期中可能经历的一系列状态以及这些状态之间的转换。这种概念广泛应用于计算机科学...

    StateMachine.zip

    在Android系统中,状态机(StateMachine)是一种设计模式,用于管理具有多个状态并根据特定条件在这些状态之间转换的对象。Android 9(Pie)中的原生状态机实现为开发者提供了强大的工具来处理复杂的逻辑和流程控制...

    Android内核驱动——电源管理

    2. **Device Power State Machine**:设备电源状态机定义了设备的各个电源状态及其转换规则。根据设备活动级别,状态机可以切换到不同的电源模式,如待机、空闲、活动等。 3. **Kernel Power Management**:内核中...

    android browser浏览器代码解析uml图

    此外,对于学习Android系统和Web开发的人来说,这也是一个很好的实践案例,能够帮助他们掌握实际项目中的设计模式和编程技巧。 总的来说,Android Browser的代码解析和UML图分析提供了一个完整的视角,让我们能够从...

    三款经典android小游戏源码分析.7z

    4. **状态机(State Machine)**:游戏通常包含多个状态(如开始、暂停、游戏结束等),状态机设计模式在此非常有用。每个游戏都会有自己的状态管理机制,确保游戏流程的正确切换。 5. **数据结构与算法**:猜牌...

    飞行射击游戏 android

    使用GameState或StateMachine模式来管理这些状态。 9. **网络功能**: 如果你想增加在线排行榜或多人对战功能,就需要集成网络编程。Android提供了Socket编程接口,也可以使用Google Play Game Services API来实现...

    安卓Android源码——(操作游戏主角).zip

    10. **状态机(State Machine)**:游戏主角可能有多种状态(如行走、跳跃、攻击),使用状态机设计模式可以帮助管理这些状态及其转换。 这个压缩包中的4-13(操作游戏主角)可能是一个逐步教学的例子,涵盖了以上提到...

    安卓状态机demo

    在安卓应用开发中,状态机(StateMachine)是一种强大的设计模式,用于管理复杂系统中的状态转换。这个"安卓状态机demo"旨在教你如何从安卓源码中提取状态机并将其应用于你的应用程序。通过学习和理解这个示例,你...

    Android系统中软件状态机的使用研究.pdf

    3. **库与框架**:如`StateMachine`库或`L匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕匕...

    Android自定义View制作动态炫酷按钮实例解析

    为了实现这些状态的切换,开发者采用了状态机(StateMachine)和代理(Delegate)模式。状态机在这里是一个接口`ButtonStatus`,它定义了获取当前状态的`getStatus()`方法,以及处理触摸事件的`onTouchEvent...

    坦克大战源码+exe_C#_DEMO_坦克大战_unity_坦克大战unity_

    为了实现游戏的循环和持久性,开发者可能会使用到Unity的State Machine(状态机)或者Finite State Machine(有限状态机)设计模式。这样可以根据游戏的不同阶段切换不同的行为模式,比如坦克的静止、移动、射击等。...

    Statelin:Kotlin和Android的有限状态机

    在Android开发中,有限状态机(Finite State Machine, FSM)是一种设计模式,它将一个对象的行为定义为一组状态,每个状态都可以转换到另一个状态。Statelin是一个专为Kotlin和Android平台设计的有限状态机库,它...

    用cocos2d-x C++开发的俄罗斯方块源码和apk

    对于游戏的逻辑,开发者可能使用了状态机(State Machine)模式来管理游戏的不同状态,如等待新方块生成、方块下落、消除行、游戏结束等。这种设计模式有助于保持代码结构清晰,易于维护。 至于APK文件,它是...

Global site tag (gtag.js) - Google Analytics