Android StateMachine记录
16lz
2021-01-23
分析例子
对StateMachine中例子进行逐步分析
其状态切换逻辑图如下:
mP1 mP2 / \ mS2 mS1 <-InitState
几条重要的概念:
- 1 State方法有enter()/exit() 分别在进入和离开当前状态执行。
- 2 需要重写State中processMessage来完成自己的状态切换逻辑。
- 3 状态机初始化的时候,在根节点到初始节点上的节点都会执行enter。且执行顺序是从根节点开始。
- 4 在进行状态切换的时候,是从目标节点向上查找跟当前的节点的共同父节点。如图:
mP1 / \ s1 s2 / \ s3 s4 <- startstate /s5 <- destate// 从目标节点 s5 向上查找,找到s1是和s4 时共同父节点。// 则从s4开始向上执行exit()方法。再从s3开始到s5 执行enter()方法。
- 4 使用deferMessage会影响消息处理的顺序
class Hsm1 extends StateMachine { public static final int CMD_1 = 1; public static final int CMD_2 = 2; public static final int CMD_3 = 3; public static final int CMD_4 = 4; public static final int CMD_5 = 5; public static Hsm1 makeHsm1() { log("makeHsm1 E"); Hsm1 sm = new Hsm1("hsm1"); sm.start(); log("makeHsm1 X"); return sm; } Hsm1(String name) { super(name); log("ctor E"); // Add states, use indentation to show hierarchy addState(mP1); addState(mS1, mP1); addState(mS2, mP1); addState(mP2); // Set the initial state setInitialState(mS1); log("ctor X"); } class P1 extends State { @Override public void enter() { log("mP1.enter"); } @Override public boolean processMessage(Message message) { boolean retVal; log("mP1.processMessage what=" + message.what); switch(message.what) { case CMD_2: // CMD_2 will arrive in mS2 before CMD_3 sendMessage(obtainMessage(CMD_3)); deferMessage(message); transitionTo(mS2); retVal = HANDLED; break; default: // Any message we don't understand in this state invokes unhandledMessage retVal = NOT_HANDLED; break; } return retVal; } @Overrid public void exit() { log("mP1.exit"); } } class S1 extends State { @Override public void enter() { log("mS1.enter"); } @Override public boolean processMessage(Message message) { log("S1.processMessage what=" + message.what); if (message.what == CMD_1) { // Transition to ourself to show that enter/exit is called transitionTo(mS1); return HANDLED; } else { // Let parent process all other messages return NOT_HANDLED; } } @Override public void exit() { log("mS1.exit"); } } class S2 extends State { @Override public void enter() { log("mS2.enter"); } @Override public boolean processMessage(Message message) { boolean retVal; log("mS2.processMessage what=" + message.what); switch(message.what) { case(CMD_2): sendMessage(obtainMessage(CMD_4)); retVal = HANDLED; break; case(CMD_3): deferMessage(message); transitionTo(mP2); retVal = HANDLED; break; default: retVal = NOT_HANDLED; break; } return retVal; } @Override public void exit() { log("mS2.exit"); } } class P2 extends State { @Override public void enter() { log("mP2.enter"); sendMessage(obtainMessage(CMD_5)); } @Override public boolean processMessage(Message message) { log("P2.processMessage what=" + message.what); switch(message.what) { case(CMD_3): break; case(CMD_4): break; case(CMD_5): transitionToHaltingState(); break; } return HANDLED; } @Override public void exit() { log("mP2.exit"); } } @Override void onHalting() { log("halting"); synchronized (this) { this.notifyAll(); } } P1 mP1 = new P1(); S1 mS1 = new S1(); S2 mS2 = new S2(); P2 mP2 = new P2();}
分析源码
想要使用状态机分三步,
初始化流程
1 继承StateMachine。2 添加状态。3 启动状态机。
class Hsm1 extends StateMachine { public static Hsm1 makeHsm1() { log("makeHsm1 E"); Hsm1 sm = new Hsm1("hsm1"); sm.start(); log("makeHsm1 X"); return sm; } Hsm1(String name) { super(name); log("ctor E"); // Add states, use indentation to show hierarchy addState(mP1); addState(mS1, mP1); addState(mS2, mP1); addState(mP2); // Set the initial state setInitialState(mS1); log("ctor X"); } ...}
- 1 添加状态
public final void addState(State state) { mSmHandler.addState(state, null); } private final StateInfo addState(State state, State parent) { ... StateInfo parentStateInfo = null; if (parent != null) { parentStateInfo = mStateInfo.get(parent); if (parentStateInfo == null) { // Recursively add our parent as it's not been added yet. parentStateInfo = addState(parent, null); } } StateInfo stateInfo = mStateInfo.get(state); if (stateInfo == null) { stateInfo = new StateInfo(); mStateInfo.put(state, stateInfo); } ... stateInfo.state = state; stateInfo.parentStateInfo = parentStateInfo; stateInfo.active = false; if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo); return stateInfo; }
添加节点的时候,需要同时传入父节点。当没有父节点的时候,不执行递推查询。生成StateInfo和State进行映射。其实StateInfo就是一个链表。parentStateInfo是指向父节点的,active表明当前的节点是否被激活。
结合调用段的代码进行分析,
//没有传入父节点,则mP1就是根节点,并将其加入mStateInfo Map中并进行映射 //其StateInfo中的parentStateInfo为null,当前的节点并灭有被激活。 addState(mP1); //传入mP1作为父节点,则从mStateInfo中取出的mP1的StateInfo //mStateInfo没有mS1的StateInfo信息,初始其StateInfo将其加入mStateInfo //其StateInfo中的parentStateInfo指向mP1,当前节点没有被激活 addState(mS1, mP1); //同mS1 添加的分析步骤 addState(mS2, mP1); //同mP1 添加的分析步骤 addState(mP2); //设置初始化的节点指向mS1,其后的代码就是简单的赋值 setInitialState(mS1);
再所有的State添加完毕后,就需要启动状态机
public static Hsm1 makeHsm1() { log("makeHsm1 E"); Hsm1 sm = new Hsm1("hsm1"); sm.start(); log("makeHsm1 X"); return sm; } //StateMachine.java 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. */ //查找最深的那个分支。换句话理解就是测量这颗树一共有多少度。 //然后会根据maxDepth 创建数组。做成类似于栈结构 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); //根据maxDepth创建数组,是保证能装下从根节点到子节点中所有的路径 mStateStack = new StateInfo[maxDepth]; mTempStateStack = new StateInfo[maxDepth]; //初始化State,做两步。1 从初始节点向上找到根节点。并将其放置在mTempStateStack //2 将mTempStateStack倒序拷贝到mStateStack setupInitialStateStack(); /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */ sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj)); if (mDbg) mSm.log("completeConstruction: X"); } //--- from completeConstruction() private final void setupInitialStateStack() { if (mDbg) { mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName()); } //拿到初始节点,(StateInfo是一个指向根节点的单向链表, curStateInfo就像是一个指针) StateInfo curStateInfo = mStateInfo.get(mInitialState); //当前的链表指向初始节点,然后向上一直到根节点。 //(mS1->mP1)因为根节点的parenStateInfo是null。 // mS1 curStateInfo != null mTempStateStackCount = 0; // mP1 curStateInfo.parentStateInfo != null mTempStateStackCount = 1; // null mP1.parentStateInfo == null mTempStateStackCount = 2; for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) { mTempStateStack[mTempStateStackCount] = curStateInfo; curStateInfo = curStateInfo.parentStateInfo; } // Empty the StateStack //记住此处将mStateStackTopIndex赋值为-1,因为后续的改变State也会用到此state mStateStackTopIndex = -1; //当前的mTempStateStack的顺序是 [mS1, mP1] 我们希望拿到顺序是[mP1, mS1] moveTempStateStackToStateStack(); } //--- from setupInitialStateStack() private final int moveTempStateStackToStateStack() { // startingIndex为0 int startingIndex = mStateStackTopIndex + 1; // mTempStateStackCount = 2, i = 1; int i = mTempStateStackCount - 1; // j = 0; int j = startingIndex; while (i >= 0) { // i=1 ===> i=0 ===> i=-1(terminal) if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j); mStateStack[j] = mTempStateStack[i]; j += 1; i -= 1; } // j = 2, mStateStackTopIndex = 1; mStateStackTopIndex = j - 1; if (mDbg) { mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex + ",startingIndex=" + startingIndex + ",Top=" + mStateStack[mStateStackTopIndex].state.getName()); } //起始还是为0 //为什么要返回这个了?是为了后面的在改变状态的是,从共同节点作为起点。 return startingIndex; } //--- from sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj)); // mSmHandlerObj是为了区分是状态机内部的消息,不是由外部进行驱动的。 // 且状态机的启动只能由内部启动 @Override public final void handleMessage(Message msg) { if (!mHasQuit) { if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) { mSm.onPreHandleMessage(msg); } if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what); /** Save the current message */ mMsg = msg; /** State that processed the message */ State msgProcessedState = null; if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) { /** Normal path */ msgProcessedState = processMsg(msg); } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) { /** Initial one time path. */ mIsConstructionCompleted = true; invokeEnterMethods(0); } else { throw new RuntimeException("StateMachine.handleMessage: " + "The start method not called, received msg: " + msg); } performTransitions(msgProcessedState, msg); // We need to check if mSm == null here as we could be quitting. if (mDbg && mSm != null) mSm.log("handleMessage: X"); if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) { mSm.onPostHandleMessage(msg); } } } //--- from handleMessage() private final void invokeEnterMethods(int stateStackEnteringIndex) { //此处mStateStackTopIndex = 1. 是在moveTempStateStackToStateStack方法中算出来的。 //stateStackEnteringIndex = 0; 此处就是从根节点向下进行初始化 [mP1, mS1] //在调用state的enter()方法, 并将mStateInfo.active 置为 true for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) { if (stateStackEnteringIndex == mStateStackTopIndex) { // Last enter state for transition mTransitionInProgress = false; } if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName()); mStateStack[i].state.enter(); mStateStack[i].active = true; } mTransitionInProgress = false; // ensure flag set to false if no methods called } //--- from handleMessage() //此方法是主要的处理方法 private void performTransitions(State msgProcessedState, Message msg) { /** * If transitionTo has been called, exit and then enter * the appropriate states. We loop on this to allow * enter and exit methods to use transitionTo. */ //orgState就是初始化节点 State orgState = mStateStack[mStateStackTopIndex].state; ... // 当前mDestState为null, 此mDestState为状态迁移的 State destState = mDestState; if (destState != null) { /** * Process the transitions including transitions in the enter/exit methods */ while (true) { if (mDbg) mSm.log("handleMessage: new destination call exit/enter"); /** * Determine the states to exit and enter and return the * common ancestor state of the enter/exit states. Then * invoke the exit methods then the enter methods. */ StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState); // flag is cleared in invokeEnterMethods before entering the target state mTransitionInProgress = true; invokeExitMethods(commonStateInfo); int stateStackEnteringIndex = moveTempStateStackToStateStack(); invokeEnterMethods(stateStackEnteringIndex); /** * Since we have transitioned to a new state we need to have * any deferred messages moved to the front of the message queue * so they will be processed before any other messages in the * message queue. */ moveDeferredMessageAtFrontOfQueue(); if (destState != mDestState) { // A new mDestState so continue looping destState = mDestState; } else { // No change in mDestState so we're done break; } } mDestState = null; } /** * After processing all transitions check and * see if the last transition was to quit or halt. */ if (destState != null) { if (destState == mQuittingState) { /** * Call onQuitting to let subclasses cleanup. */ mSm.onQuitting(); cleanupAfterQuitting(); } else if (destState == mHaltingState) { /** * Call onHalting() if we've transitioned to the halting * state. All subsequent messages will be processed in * in the halting state which invokes haltedProcessMessage(msg); */ mSm.onHalting(); } } }
执行输出的Log:
D/hsm1 ( 1999): makeHsm1 ED/hsm1 ( 1999): ctor ED/hsm1 ( 1999): ctor XD/hsm1 ( 1999): mP1.enterD/hsm1 ( 1999): mS1.enterD/hsm1 ( 1999): makeHsm1 X
测试
分析完初始化流程,执行测试案例。
Hsm1 hsm = makeHsm1();synchronize(hsm) {//1 hsm.sendMessage(obtainMessage(hsm.CMD_1));//2 hsm.sendMessage(obtainMessage(hsm.CMD_2)); try { // wait for the messages to be handled hsm.wait(); } catch (InterruptedException e) { loge("exception while waiting " + e.getMessage()); }}
- 1 外部触发
测试代码是通过sendMessage从外面进行触发,在状态机中是由mSmHandler进行处理。并将message加入到MessageQueue的消息队列中,会在handleMessage()中进行处理。
@Override public final void handleMessage(Message msg) { if (!mHasQuit) { if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) { //onPreHandleMessage是个空方法。子类可以自己实现 mSm.onPreHandleMessage(msg); } //在初始化完成后mIsConstructionCompleted = true; if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) { /** Normal path */ msgProcessedState = processMsg(msg); } ... //再执行完S1.processMessage后,当前的mDestState为mS1 performTransitions(msgProcessedState, msg); // We need to check if mSm == null here as we could be quitting. if (mDbg && mSm != null) mSm.log("handleMessage: X"); if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) { mSm.onPostHandleMessage(msg); } } } //--- from handleMessage private final State processMsg(Message msg) { //从初始状态开始 StateInfo curStateInfo = mStateStack[mStateStackTopIndex]; if (mDbg) { mSm.log("processMsg: " + curStateInfo.state.getName()); } if (isQuit(msg)) { transitionTo(mQuittingState); } else { //从初始节点调用processMessage方法。 //如果当前节点可以处理跳过,如果当前节点不可处理。则调用父节点的processMessage方法 //如果一直迭代到根节点还是无法处理此msg。则调用unhandleMessage.表示无法处理此msg 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; }
上面的代码会执行到初始节点的processMessage方法,而初始节点的是mS1。我接着分析一下S1的代码。
class S1 extends State { @Override public void enter() { log("mS1.enter"); } @Override public boolean processMessage(Message message) { log("S1.processMessage what=" + message.what); if (message.what == CMD_1) { //设置状态机的mDestState 为mS1 transitionTo(mS1); // 返回true return HANDLED; } else { // Let parent process all other messages return NOT_HANDLED; } } @Override public void exit() { log("mS1.exit"); } } //StateMachine.java private final void transitionTo(IState destState) { if (mTransitionInProgress) { Log.wtf(mSm.mName, "transitionTo called while transition already in progress to " + mDestState + ", new target state=" + destState); } mDestState = (State) destState; if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName()); }
S1中的方法中只是将状态机的mDestState设置为mS1, 接下来我们在回到hanldMessage方法中。顺着逻辑执行performTransitions()
private void performTransitions(State msgProcessedState, Message msg) { /** * If transitionTo has been called, exit and then enter * the appropriate states. We loop on this to allow * enter and exit methods to use transitionTo. */ //此处为mS1 State orgState = mStateStack[mStateStackTopIndex].state; ... //destState 为 mS1 State destState = mDestState; if (destState != null) { /** * Process the transitions including transitions in the enter/exit methods */ while (true) { if (mDbg) mSm.log("handleMessage: new destination call exit/enter"); /** * Determine the states to exit and enter and return the * common ancestor state of the enter/exit states. Then * invoke the exit methods then the enter methods. */ //从目标节点向查找跟当前节点共同的父节点。 StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState); // flag is cleared in invokeEnterMethods before entering the target state mTransitionInProgress = true; //从当前节点一直共同的父节点(不包含共同的父节点),执行exit()方法 invokeExitMethods(commonStateInfo); int stateStackEnteringIndex = moveTempStateStackToStateStack(); // 从共同的父节点一直到目标节点(不包含共同的父节点),执行enter方法 invokeEnterMethods(stateStackEnteringIndex); /** * Since we have transitioned to a new state we need to have * any deferred messages moved to the front of the message queue * so they will be processed before any other messages in the * message queue. */ //会将mDefer队列中的消息加到MessageQueue的头部。并清空 moveDeferredMessageAtFrontOfQueue(); if (destState != mDestState) { // A new mDestState so continue looping destState = mDestState; } else { // No change in mDestState so we're done break; } } mDestState = null; } /** * After processing all transitions check and * see if the last transition was to quit or halt. */ if (destState != null) { if (destState == mQuittingState) { /** * Call onQuitting to let subclasses cleanup. */ mSm.onQuitting(); cleanupAfterQuitting(); } else if (destState == mHaltingState) { /** * Call onHalting() if we've transitioned to the halting * state. All subsequent messages will be processed in * in the halting state which invokes haltedProcessMessage(msg); */ mSm.onHalting(); } } }//--- from performTransitions// p1 // / \// ^ s0 s1// | / \ \// ^ s3 s4 s5 // | / \// destState -> s6 s7 <- currentState// 如果当前节点是s7 则[P1, s0, s4, s7]为激活的节点(ative = true),// 现在目标节点是s6,从s6往上一直查到s0为激活节点。 private final StateInfo setupTempStateStackWithStatesToEnter(State destState) { /** * Search up the parent list of the destination state for an active * state. Use a do while() loop as the destState must always be entered * even if it is active. This can happen if we are exiting/entering * the current state. */ mTempStateStackCount = 0; StateInfo curStateInfo = mStateInfo.get(destState); do { mTempStateStack[mTempStateStackCount++] = curStateInfo; curStateInfo = curStateInfo.parentStateInfo; } while ((curStateInfo != null) && !curStateInfo.active); if (mDbg) { mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount=" + mTempStateStackCount + ",curStateInfo: " + curStateInfo); } return curStateInfo; }//--- from performTransitions// p1 // / \// commonStateInfo -> s0 s1// / \ \// s3 s4 s5 // / \ ^// destState -> s6 s7 <- currentState |// 从s7开始一直到s0为止,一路上的节点需要执行exit()方法,// 同时mStateStackTopIndex会向上移动,直到指向s0. /** * Call the exit method for each state from the top of stack * up to the common ancestor state. */ private final void invokeExitMethods(StateInfo commonStateInfo) { while ((mStateStackTopIndex >= 0) && (mStateStack[mStateStackTopIndex] != commonStateInfo)) { State curState = mStateStack[mStateStackTopIndex].state; if (mDbg) mSm.log("invokeExitMethods: " + curState.getName()); curState.exit(); mStateStack[mStateStackTopIndex].active = false; mStateStackTopIndex -= 1; } }//--- from performTransitions// mStateEnterTopIndex p1 // \ / \// commonStateInfo -> s0 s1// / \ \// s3 s4 s5 // / \ // destState -> s6 s7 // 从mStateEnterTopIndex向下一直到s6执行enter()方法。 /** * Invoke the enter method starting at the entering index to top of state stack */ private final void invokeEnterMethods(int stateStackEnteringIndex) { for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) { if (stateStackEnteringIndex == mStateStackTopIndex) { // Last enter state for transition mTransitionInProgress = false; } if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName()); mStateStack[i].state.enter(); mStateStack[i].active = true; } mTransitionInProgress = false; // ensure flag set to false if no methods called }
Hsm1 hsm = makeHsm1();synchronize(hsm) {//1 这个执行完毕,状态机的mDestState = mS1; hsm.sendMessage(obtainMessage(hsm.CMD_1));//2 目前执行到这个位置。 hsm.sendMessage(obtainMessage(hsm.CMD_2)); try { // wait for the messages to be handled hsm.wait(); } catch (InterruptedException e) { loge("exception while waiting " + e.getMessage()); }}//根据前面的分析,会走到StateMachine的handleMessage,先执行S1.processMessage发现无法处理//交付给P1.processMessage进行处理。所以接下来分析P1的代码。 class P1 extends State { @Override public void enter() { log("mP1.enter"); } @Override public boolean processMessage(Message message) { boolean retVal; log("mP1.processMessage what=" + message.what); switch(message.what) { case CMD_2: // CMD_2 will arrive in mS2 before CMD_3 //将CMD_3加到消息队列中 sendMessage(obtainMessage(CMD_3)); //将message(msg=CMD_2)加到defer队列中 deferMessage(message); //将mDestState设置为mS2。 transitionTo(mS2); retVal = HANDLED; break; default: // Any message we don't understand in this state invokes unhandledMessage retVal = NOT_HANDLED; break; } return retVal; } @Override public void exit() { log("mP1.exit"); } }//--- from handleMessage//将调用deferMessage的消息加到MessageQueue队列之前。 private final void moveDeferredMessageAtFrontOfQueue() { /** * The oldest messages on the deferred list must be at * the front of the queue so start at the back, which * as the most resent message and end with the oldest * messages at the front of the queue. */ for (int i = mDeferredMessages.size() - 1; i >= 0; i--) { Message curMsg = mDeferredMessages.get(i); if (mDbg) mSm.log("moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what); sendMessageAtFrontOfQueue(curMsg); } mDeferredMessages.clear(); }
以上执行是 hsm.sendMessage(obtainMessage(hsm.CMD_1)); 的流程,执行往后输出的log的如下:
D/hsm1 ( 1999): mS1.processMessage what=1D/hsm1 ( 1999): mS1.exitD/hsm1 ( 1999): mS1.enter
后面的代码 hsm.sendMessage(obtainMessage(hsm.CMD_2)); 也是走相同的流程,只不过需要结合不同的State中processMessage代码进行分析。
总结
综上整体的状态机流程如下:
- 1 设置不同State之间的父子关系。
- 2 设定初始State
- 3 启动状态机
- 4 重写State processMessage 定义状态切换的逻辑
- 5 transitationTo() 来切换下一个目标状态
- 6 可以通过deferMessage()来改变消息的处理优先级。
更多相关文章
- Android PopupWindow 隐藏软键盘的方法
- Android 访问Http被限制解决方法
- 禁止Edittext弹出系统软键盘 的几种方法
- Android Studio:正确引入so文件的方法
- android 学习五 设置应用程序全屏(没有状态栏和标题栏)
- Android studio编译时出现aapt.exe 崩溃的解决方法
- listView中item 图文并存的两种方法