源码分析之状态机原型
许久没更新设计模式部分内容了,之前介绍了设计模式中的状态设计模式,期间一直忙于工作上的事,对安卓源码进行了相关的学习。翻回来看时候,发现更新到这里的时候,就顺便对源码部分的状态机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详细说明
-
几个重要的内部成员 HashMap
mStateInfo 状态树成员,以HashMap形式存放,键State,值StateInfo State mInitialState 初始化状态信息 State mDestState 带转换的目的状态 ArrayList
mDeferredMessages 延迟消息列表 HaltingState mHaltingState 特殊状态:状态机停止状态(内部自建) QuittingState mQuittingState 特殊状态:状态机正在停止中状态(状态机内部自建) StateInfo mStateStack[]; 状态树中从顶层到初始状态信息的信息栈 StateInfo mTempStateStack[]; 状态树中初始状态到顶层状态信息的信息栈 复制代码 -
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); ... } 复制代码
-
状态处理
// 对应状态(底层)处理当前消息 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; } 复制代码
-
遍历状态树,各自调用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; } } 复制代码
-
转移状态,更新状态树
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(); } } } 复制代码
-
查找目标状态下对应的状态树(排除共有的状态树部分)
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; } 复制代码
-
根据当前状态树顶层状态,依次调用其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; } } 复制代码
-
逆序整理两个状态栈信息
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; } 复制代码
-
依次调用enter方法
详见前4部分 private final void invokeEnterMethods(int stateStackEnteringIndex) { ... } 复制代码
-
更改状态,直接设置中间变量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); ... } } 复制代码
-
具体实现方式:
- 新建自己的状态机继承于系统的StateMachine状态机,实现其相应的方法
- 完成自己需求中各种状态的具体逻辑实现,其必须继承自系统的State,具体逻辑实现在其自身的pressageMessage方法中。
- 依据示例代码,首先构建状态机,添加对应的状态层级组成状态树,由状态机维持,初始化状态机,开启状态机即可
-
总结:以上基本分析了StateMachine状态机原型的工作方式和原理,其内部维持一个状态树,当达到当前状态时,由维持当前状态的实例对象处理对应的逻辑,这便对应了不同状态下对同一个请求有不同的处理方式,当前状态不处理遍向上传递父类调用,有点类似安卓中触摸事件的向上传递机制。
相关拓展
设计模式之状态模式