Android StateMachine 分析

        State模式允许一个对象在期内部状态改变时改变其行为模式,State 模式是将对象的状态封装成一个对象,是在不同的状态下执行不同的操作。Android系统通过StateMachine 是State模式的一种应用,其实现的相当精妙。本文对StateMachine做个深入分析,介绍其实现机制。

         首先通过一个例子来引入问题,假如我们有6个状态S1、S2、S3、S4、S5、S6。其中:1、每个状态对某些消息都要做不同的处理;2、S1是S2的父状态,S2是S3的父状态,S3是S4的父,S1是S5的父状态,S5是S6的父状态;3、初始状态是S4;4、在运行过程中,收到某个消息时状态机有S4转到S3。如下图:

        

                   图1:状态结构图

        图1显示了个状态的关系,在解析Android StateMachine如何管理State之前,先介绍State。

State:

public class State implements IState {

    
    protected State() {
    }
 
    @Override
    public void enter() {
    }

    @Override
    public void exit() {
    }

    @Override
    public boolean processMessage(Message msg) {
        return false;
    }

    @Override
    public String getName() {
        String name = getClass().getName();
        int lastDollar = name.lastIndexOf('$');
        return name.substring(lastDollar + 1);
    }
}
         State 作为状态基类,StateMachine 管理的state都需要从此继承下来。重写几个重要方法,enter表示进入该状态的调用,exit是退出该状态会调用,processMessage是当前状态下处理消息的函数。如果当前状态处理后返回false表明还需要父状态处理,如图1中示例,如果状态S4处理消息后返回false表明还需要其父状态S2处理,直到递归到根状态。通过这样的机制为设计状态机的各种状态提供了便利,对于一些公共消息可以放到公共状态上来处理,后面会介绍其实现的机制。

         在Android Handler机制博文中介绍了Android线程处理消息的一种方式,在StateMachine中,核心也是采用了类似的机制来管理states。StateMachine通过一独立线程来完成三个功能

  • 建立树型状态层次结构
  • 状态机状态堆栈的建立并管理状态切换
  • 分发消息

         以上三个功能均有mSmHandler来完成,mSmHandler类继承于Handler,下面来分析其如何完成以上三个功能。


1. 建立树形结构层次

       StateMachine 提供AddState方法来添加状态到状态机中,函数如下:

  protected final void addState(State state, State parent) {
        mSmHandler.addState(state, parent);
    }
       这里其实调用的是mSmHandler的addState 方法,再网下跟踪

  private final StateInfo addState(State state, State parent) {
            // 获取当前状态父状态信息
            StateInfo parentStateInfo = null;
            if (parent != null) {
                parentStateInfo = mStateInfo.get(parent);
                if (parentStateInfo == null) {
                    // 当父状态未添加进来时,递归添加进来.
                    parentStateInfo = addState(parent, null);
                }
            }
            // 判断状态信息是否添加到树形结构中
            StateInfo stateInfo = mStateInfo.get(state);
            if (stateInfo == null) {
                stateInfo = new StateInfo();
                mStateInfo.put(state, stateInfo);
            }

            // Validate that we aren't adding the same state in two different hierarchies.
            if ((stateInfo.parentStateInfo != null) &&
                    (stateInfo.parentStateInfo != parentStateInfo)) {
                    throw new RuntimeException("state already added");
            }
            stateInfo.state = state;
            stateInfo.parentStateInfo = parentStateInfo;
            stateInfo.active = false;
            if (mDbg) Log.d(TAG, "addStateInternal: X stateInfo: " + stateInfo);
            return stateInfo;
        }

     在StateMachine类中,StateInfo就是一个包装State的树节点,每一个状态对应一个状态信息StateInfo,通过HashMaps存储下来

 private HashMap<State, StateInfo> mStateInfo =new HashMap<State, StateInfo>();
     因此图1所的示状态构成可以通过以下流程来实现:

     addState(S1,null);addState(S2,S1);addState(S3,S2);addState(S4,S2);addState(S5,S1);addState(S6,S5);


2. 状态机堆栈的建立和状态切换

          2.1 状态机堆栈建立

         StateMachine 通过两个结构来建立和切换状态堆栈, mStateStack[] 及  mTempStateStack[]。建立堆栈API如下:

 private final void completeConstruction() {
          
             // 计算堆栈深度,状态个数
            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;
                }
            }
            // 建立两个栈
            mStateStack = new StateInfo[maxDepth];
            mTempStateStack = new StateInfo[maxDepth];
            //根据初始状态构建父子关系栈
             setupInitialStateStack();

           // 激活对应状态,并调用State的entry方法表明进入当前状态
            mIsConstructionCompleted = true;
            mMsg = obtainMessage(SM_INIT_CMD);
            invokeEnterMethods(0);

            //如果需要将做状态切换,在后面会介绍
            performTransitions();

        }
          在来分析setUpInitialStateStack()的实现。

 private final void setupInitialStateStack() {
            // 根据初始状态建立临时栈,如本文开始提出的初始状态为S4,那么临时栈存储的状态为:S4->S2->S1
            StateInfo curStateInfo = mStateInfo.get(mInitialState);
            for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
                mTempStateStack[mTempStateStackCount] = curStateInfo;
                curStateInfo = curStateInfo.parentStateInfo;
            }

            // Empty the StateStack
            mStateStackTopIndex = -1;
            // 将临时栈数据反过来存到state 栈中,即S1->S2->S4 来保持堆栈先进后出的机制
            moveTempStateStackToStateStack();
        }
        2.2 状态切换

        SateMachine 提供的方法是:

protected final void transitionTo(IState destState) {
        mSmHandler.transitionTo(destState);
    }
       mSmHandler 提供的方法是:
 private final void transitionTo(IState destState) {
            mDestState = (State) destState;
        }
       从中可以看出mSmHandler只是记录了一下切换到的目的状态,真正的切换实现在performTransitions 中。
 private void performTransitions() {
           
            State destState = null;
            while (mDestState != null) {
               
                destState = mDestState;
                mDestState = null;

                //当前状态切换了存在于mStateStack中的State需要改变
           //仍然按照链式父子关系来存储
           //先从当前状态S4找到 最近的被激活的parent状态S4
           //未被激活的全部保存起来(S3) 返回S3
                StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                invokeExitMethods(commonStateInfo);
                // 将临时栈的state修改到State栈中
                int stateStackEnteringIndex = moveTempStateStackToStateStack();
                invokeEnterMethods(stateStackEnteringIndex);
            }

        }
       mTempStateStack 原来的数据是: S4-S2-S1;mStateStack状态切换前是:S1-S2-S4 栈顶是2状态切换到S3后,mTempStateStack的数据是:S3-S2-S1;mStateStack状态是S1-S2-S3。

3. 消息分发

          mSmHandler实际是一Handler对象,消息处理函数在handleMessage方法中。

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){
      //此消息未被当前层次状态链处理
    }
  }
}

       SateMachine 通过堆栈来管理State一是方便状态切换,二是实现消息的链式处理,功能强大。除此之外,SateMachine还提供处理完消息记录功能及延时消息处理功能,

在这里就不再介绍了。参考方法deferMessage,ProcessedMessages的实现。



      

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值