Android StateMachine记录

设计模式(21) 状态模式 状态模式允许一个对象在其内部状态改变时改变它的行为。用电梯来举例,电梯可以认为具有开门、关门、运行、停止四种状态,这四种状态之间的切换具有多种限制,比如在开门状态下不电梯不能运行,只能转为关门状态;在运行状态下,电梯只能转为停止状态… 设想一下,如果要常规的if-else或者switch-case描述电梯的这几种状态间的切换,将生成非常复杂的、逻辑相互交织的代码,可读性差且不易维护。 而如果用状态模式来实现,会是怎样的呢? 首先创建LiftState,代表抽象的电梯状态,包含了电梯的四个动作(方法),通过这 阅读详情

分析例子

对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 E
D/hsm1    ( 1999): ctor E
D/hsm1    ( 1999): ctor X
D/hsm1    ( 1999): mP1.enter
D/hsm1    ( 1999): mS1.enter
D/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=1
D/hsm1    ( 1999): mS1.exit
D/hsm1    ( 1999): mS1.enter

后面的代码 hsm.sendMessage(obtainMessage(hsm.CMD_2)); 也是走相同的流程,只不过需要结合不同的State中processMessage代码进行分析。

总结

综上整体的状态机流程如下:

  • 1 设置不同State之间的父子关系。
  • 2 设定初始State
  • 3 启动状态机
  • 4 重写State processMessage 定义状态切换的逻辑
  • 5 transitationTo() 来切换下一个目标状态
  • 6 可以通过deferMessage()来改变消息的处理优先级。
开启WIFI的StateMachine&WifiController的LOG StateMachine.java (z:\home\android\work\304\android\frameworks\base\core\java\com\android\internal\util): if (mDbg && mSm != null) mSm.log("handleMessage: X"); StateMachine.jav... 阅读详情

相关推荐

Android手机上网实现机制

本章介绍DataConnectionTracker对DataConnection手机上网数据连接的管理机制和流程。一、Android网络整体结构        流程结构图如下:        Android网络中,在底层实现了两个虚拟串口:Command命令通道:建立或断开网络连接Data数据通道:基于TCP/IP网络通信协议的数据传输 [可分析下基于TCP/IP网络通信协议的数据传输实现机制]二...

hanyr1208的博客 4937

Android StateMachine学习

2018年01月06日14:33:58,西安下了三天的大雪了,上班上学上街都不方便,索性在家看看代码吧,已经好久没有更新博客了,是因为之前的笔记本配置实在跟不上了,2018年元旦,狠下心来新买了一个某米的笔记本,除了存储256G不够使用(单单下载一套Android代码,编译的时候就没有空间了,可怜的只剩下40M了,只是中通快递很不给力,扩展的硬盘还在路上。。。。。)废话不多说了,下面进入正题 此次关

mockingbirds的专栏 5190

【安卓Framework学习】Wifi框架学习之状态机流转与消息驱动机制

本文深入解析安卓Framework中Wifi框架的状态机流转与消息驱动机制,详细介绍了从setWifiEnabled()开始的Wifi开启与关闭流程。通过源码分析,揭示WifiController和ClientModeManager如何协作完成状态切换,以及分层状态机设计和消息驱动机制的优势,帮助开发者更好地理解和调试Wifi功能。

weixin_33806859的博客 154

Android StateMachine分析

Android StateMachine的官方翻译

5190

RapidScada在windows下的安装(详细)

1.下载地址:RapidScada官网 2.版本说明 有Linux版本和Windows版本,看自己的需求下载,推荐下载最新稳定版本。 3.系统要求 4.安装前的准备(windows10) 首先打开Windows功能: 先关闭 Microsoft .NET Framework3.5的 Windows Communication Foundation 子组件。如下图: Microsoft .NET Framework配置按下图勾选: iis安装的功能: 5.安装 解压..

m0_51275803的博客 4262

生产环境下oracle19c rac恢复节点2

现状:目前节点1数据库集群服务处于正常,节点2服务器由于系统故障无法恢复,确认系统重装。目标:不影响业务的情况下,重新把节点2添加回集群中。

sqlora的专栏 865

Android状态机的简单理解

Android状态机

m0_53186541的博客 1902

CoMP 协同多点传输技术

CoMP CoMP是什么 解决什么问题 有什么缺点 CoMP(Coordinated Multiple Points)协同多点传输,通信术语,LTE-A的重要技术之一。 CoMP传输是指地理位置上分离的多个传输点,协同参与为一个终端的数据(PDSCH)传输或者联合接收一个终端发送的数据(PUSCH),参与协作的多个传输点通常指不同小区的基站。CoMP技术将边缘用户置于几个基站的同频率上,几...

ilean_J的博客 2万+

Android中的状态机(续)

上周有点累,这次终于把未完成的代码给写完了,并调试完毕。   代码仍有待改进的地方: 1. 内部类的使用,提高代码的隐藏性以及类之间的密切关联程度(外围类与内部类之间的互访问)。 2. 其它的,希望大家多多提出建议。 联系 QQ 402320131   上次承诺要谈谈

liuyangsyouxiang的专栏 2934

深入解析AndroidManifest.xml与device_admin.xml:打造完美DeviceOwner应用配置

本文深入解析了Android企业级开发中DeviceOwner应用的核心配置文件AndroidManifest.xml与device_admin.xml。详细阐述了如何精准声明DeviceAdminReceiver、定义管理策略,并提供了通过ADB命令进行设置与验证的实战指南,帮助开发者构建权限完整、配置正确的设备所有者应用。

ice55的博客 679

推荐系统:NDCG评价指标及其Python实现方法

除了推荐任务,在二分类任务中,也可以计算该指标,代码如下: # ndcg def get_dcg(y_pred, y_true, k): #注意y_pred与y_true必须是一一对应的,并且y_pred越大越接近label=1(用相关性的说法就是,与label=1越相关) df = pd.DataFrame({"y_pred":y_pred, "y_true":y_true}) ...

lanyuelvyun的博客 8240

Android官方实现的层次状态机Hierarchical State Machine源代码

在刚刚那份学习笔记里包含了下面知识点所有内容!如果你正愁这块不知道如何学习或者想提升学习这块知识的学习效率,那么这份学习笔记绝对是你的秘密武器!如果你正愁这块不知道如何学习或者想提升学习这块知识的学习效率,那么这份学习笔记绝对是你的秘密武器!加入社区》https://bbs.youkuaiyun.com/forums/4304bb5a486d4c3ab8389e65ecb71ac0。加入社区》https://bbs.youkuaiyun.com/forums/4304bb5a486d4c3ab8389e65ecb71ac0。

2401_87022248的博客 780

(代码可运行)半导体器件和工艺模拟 使用Silvaco设计构建NMOS并提取各项工艺及器件参数

win10。

qq_20388971的博客 108

Android StateMachine

Android 状态机的主要作用是管理状态和处理消息。它通过定义一组状态和状态之间的转换关系,来控制系统的行为。当系统接收到消息时,StateMachine会根据当前的状态和消息类型,执行相应的操作或状态转换。在使用Android 状态时,通常需要定义一组状态和状态之间的转换关系。可以使用addState()方法来添加状态,并使用addTransition()方法来定义状态之间的转换关系。

liuning1985622的博客 1773

使用阿里天池平台训练YOLO目标检测模型

YOLO算法是一种单阶段目标检测算法,由Joseph Redmon等人于2015年提出。与传统的两阶段检测算法相比,YOLO将目标检测问题转化为回归问题,直接预测边界框的位置和类别概率,大大提高了检测速度。YOLOv1: 首次提出YOLO的核心思想,将图像分割成网格,每个网格预测边界框和类别概率。YOLOv2: 引入了批量归一化、锚框等改进,提高了检测精度。YOLOv3: 采用多尺度预测,增强了对小目标的检测能力。

步子哥的博客 1087

Android14 SystemUI如何实现下拉菜单栏高斯模糊背景效果(类似于毛玻璃效果)

我们都知道,从Android12起,下拉菜单栏控制中心都是黑色的,看起来非常不美观,而且国内的手机系统UI都做的比较美观,大部分的下拉菜单UI背景都是实现了高斯模糊效果,所以客户也会要求实现类似于这样的效果。在以前的Android项目中,实现下拉菜单栏的模糊背景就是通过截图的方式进行替换背景,而这种方式下拉菜单背景极易替换不了,就算是在各个地方都适配,还是有概率在复杂场景或者一些测试手法下出现背景是黑的情况,并且这种方式还比较影响性能,毕竟每次都要去截图一次,多做了额外的事。

热衷于Android Framework和系统APP的修改经验分享 1793

[Android]StateMachine介绍(一)——概览

StateMachine介绍 StateMachine,中文名状态机。顾名思义,是用于记录、切换状态的。除此之外,StateMachine还可以通过预定义执行的方法,使其在状态切换开始前、过程中、结束后分别执行不同的逻辑; 接下来就对StateMachine进行一个简单的拆解; 注意,如下所有代码片段均不是源代码截取,而是经过了各种删减的,删减包括但不限于日志输出部分、不影响代码逻辑分析的分支判断等;同时,为了方便阅读、理解,对原有的英文注释也进行了删减,并添加了部分中文的,基于个人的理解; 因此请勿直接使

Ryan ZHENG的专栏 1059

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

安卓StateMachine学习笔记--待续

StateMachine Android

SoniaGoGo的博客 983

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Android wifi-framework StateMachine和AsyncChannel 学习

android中,wifi的核心是WPAS(wpa_supplicant),它管理和控制Android平台中的Wi-Fi功能。在整个wifi模块中,其更像是一个一个服务端,实现了连接,认证等工作流程。 而客户端有wpa_cli,和wpa_supplicant之间的通信依靠wpa ctrl接口来实现,而在wifi的framework层,其实主要工作就是向下实现wpa_cli的功能和wpa_suppl

myvest的专栏 1002

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Android StateMachine 学习

今天学习记录下对状态机的学习 一、状态机使用的步骤 可以将源码的frameworks/base/core/java/com/android/internal/util里面把StateMachine.java 、State.java 、IState.java复制到project目录中 自定义StateMachine类 (如 MyLife) 继承StateMachine 自定义状态State (...

shiluohuashengmi的专栏 492

安卓StateMachine分析举例---WifiStateMachine

Android WifiStateMachine举例分析

SoniaGoGo的博客 3209

Android R WiFi热点流程浅析

Android上的WiFi SoftAp功能是用户常用的功能之一,它能让我们分享手机的网络给其他设备使用。 那Android系统是如何实现SoftAp的呢,这里在FWK层面做一个简要的流程分析,供自己记录和大家参考。 以Android R版本为例......

To be or not to be, it's a question 4110

andorid状态机StateMachine

状态机的代码android并没有开放使用,在源码的frameworks/base/core/java/com/android/internal/util里StateMachine.java 、State.java 、IState.java,就这三个文件,看过源码后表示设计很精妙,必须记录下来。 一、状态机我的理解是是可以更根据当前的状态和外界的的信息输入,采取不同的对应和状态的切换。androi

qinxue24的博客 513

Android状态机源码分析

转载自:http://blog.youkuaiyun.com/yangwen123/article/details/10591451

九天小哥的专栏 533

Seed-VC 零样本语音转换怎么选:4 种模型选型与参数配置指南

Seed-VC 是一款零样本语音转换工具:给它一段 1~30 秒的参考音频,就能把任意语音换成目标音色,还支持实时转换和歌声转换。它放出 4 个定位不同的模型,实时 vs 离线怎么选、参数怎么配,本文一次讲清。 ## 30 秒速查:4 个模型差异一览 | 模型 | 定位 | 采样率 | 参数规模 | 延迟表现 | 一句话结论 | | --- | --- | --- | --- | --- |

gitblog_00930的博客 730

android wifi flow

  复习并记录下android wifi相关知识,因为下载的是androidp 的source code,因此codebase就用androidP的版本,复习整个android wifi的flow,我大概会分为以下几个部分来讲解 1 android wifi enable flow 2 androi wifi scanning flow 3 android wifi user connec...

just do IT 755
下一篇: Bluetooth L2CAP 学习
leoStack
博客等级 码龄11年 16粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值