源码分析之状态机原型

源码分析之状态机原型

许久没更新设计模式部分内容了,之前介绍了设计模式中的状态设计模式,期间一直忙于工作上的事,对安卓源码进行了相关的学习。翻回来看时候,发现更新到这里的时候,就顺便对源码部分的状态机StateMachine研究一下,其内部的设计思路就是状态者模式的最佳体现,后续会这个基础上详细分说wifiStateMachine的工作方式,是状态机的具体实现方式,基本上到这里,状态模式基本就讲清楚了。

  • 重要类:StateMachine

  • 代码位置:frameworks/base/core/java/com/android/internal/util/StateMachine.java

  • 状态机,顾名思义,就是维持状态的机器。面对日常生活,我们经常面临很多种状态情况,比如财务就是负责财务收支,工资核对发放;保安就负责日常安保等等。每个职业对应自己的工作职责和状态。一个工作内容到了财务和保安都不一样,各自对应自己的职责和状态。虽说这样比喻不大恰当,但确实是这么回事,这便是状态的初始原型。状态机内部维持一个状态树,状态树保存了每个状态下各自实例。每个状态都继承同一个基类Status,实现接口IState,有四个方法enter(),exit(),processMessage(),getName()。状态机中每一个子类都要实现这几个方法,对应到各自实例就会调用自己对应的方法,这便是状态模式的作用:将请求的处理封装到状态类中,在不同状态下对同一个请求进行不同的处理。

  • 状态树:状态类的层级结构。状态机中的每个状态不能随便转换。

      		A		B
      	C		D	
      E		F
      	
      上面展示的状态树中,A状态可以直接转成B状态,相邻状态可以直接转换(注意是相邻,直接转换)。
      如果从状态E准换成B,那么要经历一下状态:E -> C, C -> A, A -> B;
      为什么要这么做呢?不妨可以这样理解,坐电梯时候,你在门外可以进入电梯和不进去;一旦进入电梯并且电梯运行中,你就不能随意进出了,完成出电梯需要有一个过程。你需要让电梯到达指定楼层,电梯停止运动,电梯开门后,你才能出电梯。
      将这种逻辑抽象出来遍生成了状态树,状态树中每个状态都有两个相邻状态,先前状态,后继状态,当然这不是必需元素。
    复制代码
  • 状态机范例

      class HelloWorld extends StateMachine {
          HelloWorld(String name) {
              super(name);
              addState(mState1);
              setInitialState(mState1);
          }
      
          public static HelloWorld makeHelloWorld() {
              HelloWorld hw = new HelloWorld("hw");
              hw.start();
              return hw;
          }
      
          class State1 extends State {
              @Override public boolean processMessage(Message message) {
                  log("Hello World");
                  return HANDLED;
              }
          }
          State1 mState1 = new State1();
      }
      
      void testHelloWorld() {
          HelloWorld hw = makeHelloWorld();
          hw.sendMessage(hw.obtainMessage());
      }
      
      1. State1继承基类State,实现processMessage处理对应自己的逻辑
      2. HelloWorld继承基类StateMachine状态机,构造函数中加入状态组成状态树,设置初始状态。
      3. makeHelloWorld中为状态机的实例过程,其实例化自身对象,对切调用状态机的start开启状态机。
      4. 测试代码中实例化状态机,状态机发送消息并且处理。
    复制代码
  • State原型

      public class State implements IState {
      
          protected State() {
          }
    
          @Override
          public void enter() {
          }
      
          @Override
          public void exit() {
          }
      
          @Override
          public boolean processMessage(Message msg) {
              return false;
          }
      
          public String getName() {
              String name = getClass().getName();
              int lastDollar = name.lastIndexOf('$');
              return name.substring(lastDollar + 1);
          }
      }
    复制代码

State原型实现IState接口,需要开发者自建对应具体的状态实现其具体逻辑,由状态树统一管理,再交予状态机调用对应状态下具体方法。

StateMachine具体分析
  • 构造函数

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

构造函数中可以看出,StateMachine内部可以自己维持HandlerThread,实现looper对象的消息处理,也可以在构造中传入。之后便进入初始化状态机。

  • 初始化状态机

      private void initStateMachine(String name, Looper looper) {
          mName = name;
          mSmHandler = new SmHandler(looper, this);
      }
    复制代码

初始化状态机中,其内部又一个重要的内部类SmHandler,其继承Handler,父类很多方法都一度封装在SmHandler内部中,我们注重介绍一下SmHandler

  • 开启状态机

      public void start() {
          
          SmHandler smh = mSmHandler;
          if (smh == null) return;
          
          smh.completeConstruction();
      }
    复制代码
  • 完成状态树信息以及初始化状态信息栈

      private final void completeConstruction() {
              if (mDbg) mSm.log("completeConstruction: E");
    
          /**
           * Determine the maximum depth of the state hierarchy
           * so we can allocate the state stacks.
           */
          // 获取状态的最深层级
          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;
              }
          }
          if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth);
    
          // 新建两个状态栈
          //1. 顶层到当前状态信息数组
          mStateStack = new StateInfo[maxDepth];
          //2. 当前状态信息到顶层状态信息数组
          mTempStateStack = new StateInfo[maxDepth];
          setupInitialStateStack();
    
      		// 置前消息,初始化消息SM_INIT_CMD,由自己的handlerMessage处理
          sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
    
          if (mDbg) mSm.log("completeConstruction: X");
      }
    复制代码
SmHandler详细说明
  1. 几个重要的内部成员 HashMap<State, StateInfo> mStateInfo 状态树成员,以HashMap形式存放,键State,值StateInfo

     State mInitialState
     初始化状态信息
     
     State mDestState
     带转换的目的状态
     
     ArrayList<Message> mDeferredMessages
     延迟消息列表
     
     HaltingState mHaltingState
     特殊状态:状态机停止状态(内部自建)
        
     QuittingState mQuittingState
     特殊状态:状态机正在停止中状态(状态机内部自建)
     
     StateInfo mStateStack[];
     状态树中从顶层到初始状态信息的信息栈
     
     StateInfo mTempStateStack[];
     状态树中初始状态到顶层状态信息的信息栈
    复制代码
  2. handlerMessage方法分析

    该方法的主要划分三部分,第一部分分配到对应的状态,由对应的状态进行处理;第二部分是状态的初始化,执行初始化状态路径上每个状态的enter方法;第三部分是执行状态转移,即更新状态树。

     public final void handleMessage(Message msg) {
             
             ...
             
         if (mIsConstructionCompleted) {
         
             // 分配到对应的状态,由对应的状态进行处理,返回执行消息的状态信息
             msgProcessedState = processMsg(msg);
         } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
                 && (mMsg.obj == mSmHandlerObj)) {
                 
             // start方法后,直接执行,执行状态初始化(执行状态树上的enter方法)
             mIsConstructionCompleted = true;
             invokeEnterMethods(0);
         } else {
             throw new RuntimeException("StateMachine.handleMessage: "
                     + "The start method not called, received msg: " + msg);
         }
         
         // 状态转移,更新状态树
         performTransitions(msgProcessedState, msg);
     		
     		...
     		
     }
    复制代码
  3. 状态处理

      // 对应状态(底层)处理当前消息
         private final State processMsg(Message msg) {
             StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
             if (mDbg) {
                 mSm.log("processMsg: " + curStateInfo.state.getName());
             }
    
         if (isQuit(msg)) {
             transitionTo(mQuittingState);
         } else {
             // 递归调用处理消息
             while (!curStateInfo.state.processMessage(msg)) {
                 /**
                  * Not processed
                  */
                 // 底层没有处理,移交父级节点处理
                 curStateInfo = curStateInfo.parentStateInfo;
                 // 始终没处理
                 if (curStateInfo == null) {
                     /**
                      * No parents left so it's not handled
                      */
                     mSm.unhandledMessage(msg);
                     break;
                 }
                 if (mDbg) {
                     mSm.log("processMsg: " + curStateInfo.state.getName());
                 }
             }
         }
         return (curStateInfo != null) ? curStateInfo.state : null;
     }
    复制代码
  4. 遍历状态树,各自调用enter方法

     private final void invokeEnterMethods(int stateStackEnteringIndex) {
         // 从顶层状态树遍历到当前状态下多有的enter方法,置位active为true(对应enter方法以调用)
         for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
             if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
             mStateStack[i].state.enter();
             mStateStack[i].active = true;
         }
     }
    复制代码
  5. 转移状态,更新状态树

     private void performTransitions(State msgProcessedState, Message msg) {
     
         // 原来的状态信息(即mStateStack数组的顶层状态信息)
         State orgState = mStateStack[mStateStackTopIndex].state;
    
         ...
    
         State destState = mDestState;
          // 目标状态不为空
         if (destState != null) {
             while (true) {
                 if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
    
                 // 查找目标状态下对应的状态树(排除共有的状态树部分)
                 StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                 // 根据当前状态树顶层状态,依次调用其enter,置位active
                 invokeExitMethods(commonStateInfo);
                 // 逆序整理两个状态栈信息
                 int stateStackEnteringIndex = moveTempStateStackToStateStack();
                 // 依次调用enter方法
                 invokeEnterMethods(stateStackEnteringIndex);
    
                 // 将延迟的消息放入到队列前面,直接处理
                 moveDeferredMessageAtFrontOfQueue();
    
                 if (destState != mDestState) {
                     // A new mDestState so continue looping
                     destState = mDestState;
                 } else {
                     // No change in mDestState so we're done
                     break;
                 }
             }
             mDestState = null;
         }
    
         // 特殊状态处理
         if (destState != null) {
             if (destState == mQuittingState) {
                 mSm.onQuitting();
                 cleanupAfterQuitting();
             } else if (destState == mHaltingState) {
               haltedProcessMessage(msg);
                  */
                 mSm.onHalting();
             }
         }
     }
    复制代码
  6. 查找目标状态下对应的状态树(排除共有的状态树部分)

     private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
         mTempStateStackCount = 0;
         // 获取目标状态
         StateInfo curStateInfo = mStateInfo.get(destState);
         do {
             /* 遍历状态树,从目标状态依次放入
              * 排除不可调用enter方法的状态(activit=false)
              * temp数组存放的顺序是有底层到顶层,调用时候应该逆序调用
              * 返回当前数组的末端数据(目标状态树下顶层状态信息)
              */
    
             mTempStateStack[mTempStateStackCount++] = curStateInfo;
             curStateInfo = curStateInfo.parentStateInfo;
         } while ((curStateInfo != null) && !curStateInfo.active);
    
         if (mDbg) {
             mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
                     + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
         }
         return curStateInfo;
     }
    复制代码
  7. 根据当前状态树顶层状态,依次调用其enter,置位active

     private final void invokeExitMethods(StateInfo commonStateInfo) {
         while ((mStateStackTopIndex >= 0)
                 && (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
             // mStateStackTopIndex存放的顶层到底层的状态树信息,依次调用
             State curState = mStateStack[mStateStackTopIndex].state;
             if (mDbg) mSm.log("invokeExitMethods: " + curState.getName());
             curState.exit();
             mStateStack[mStateStackTopIndex].active = false;
             mStateStackTopIndex -= 1;
         }
     }
    复制代码
  8. 逆序整理两个状态栈信息

     private final int moveTempStateStackToStateStack() {
         // 将mTempStateStack数组元素逆序保存到mStateStack中
         int startingIndex = mStateStackTopIndex + 1;
         int i = mTempStateStackCount - 1;
         int j = startingIndex;
         while (i >= 0) {
             if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
             mStateStack[j] = mTempStateStack[i];
             j += 1;
             i -= 1;
         }
    
         mStateStackTopIndex = j - 1;
         if (mDbg) {
             mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
                     + ",startingIndex=" + startingIndex + ",Top="
                     + mStateStack[mStateStackTopIndex].state.getName());
         }
         return startingIndex;
     }
    复制代码
  9. 依次调用enter方法

     详见前4部分
     
     private final void invokeEnterMethods(int stateStackEnteringIndex) {
     
     ...
     
     }
    复制代码
  10. 更改状态,直接设置中间变量mDestState,handlerMessage轮训中不断调用performTransitions实现状态更改

    private final void transitionTo(IState destState) {
        mDestState = (State) destState;
        if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
    }
    
    消息轮训处理不断调用,更改状态
    public final void handleMessage(Message msg) {
        if (!mHasQuit) {
            if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
            
    			...
            
            // 状态转移,更新状态树
            performTransitions(msgProcessedState, msg);
    
            ...
            
        }
    }
    复制代码

  • 具体实现方式:

    1. 新建自己的状态机继承于系统的StateMachine状态机,实现其相应的方法
    2. 完成自己需求中各种状态的具体逻辑实现,其必须继承自系统的State,具体逻辑实现在其自身的pressageMessage方法中。
    3. 依据示例代码,首先构建状态机,添加对应的状态层级组成状态树,由状态机维持,初始化状态机,开启状态机即可
  • 总结:以上基本分析了StateMachine状态机原型的工作方式和原理,其内部维持一个状态树,当达到当前状态时,由维持当前状态的实例对象处理对应的逻辑,这便对应了不同状态下对同一个请求有不同的处理方式,当前状态不处理遍向上传递父类调用,有点类似安卓中触摸事件的向上传递机制。

相关拓展

设计模式之状态模式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值