实战StateMachine状态机

实战StateMachine状态机

前面我们讲解了设计模式中的状态模式,以及分析了安卓源码中的StateMachine状态机的内部实现原理其原理简而言之:就是通过状态树维持各种状态实例,各个状态实例相邻之间可以互相转换,不相邻的便通过内部状态树遍历查找。通过消息传递机制,发送消息并转换状态实例,从而实现对应状态下响应对应状态下应有的逻辑。

状态机的实现步骤

  1. 自定义StateMachine类继承StateMachine
  2. 自定义状态State继承State:重写enter、processMsg、exit
  3. 设置更新状态常量,用于状态更新
  4. addState加入父状态、子状态,setInitialState初始化状态
  5. start启动状态机stateMachine
  6. 使用状态机,发送持有更新状态常量的Msg,处理对应的pressageMessage

示例代码

  • 我们以电梯运行为例实现状态机的实现过程
  1. 状态分析:

    待机    停机
    等待选择(上楼or下楼)
    运行中
    到站
    
  2. 自定义LiftStateMachine继承StateMachine

    public class LiftStateMachine extends StateMachine {
    
        private static LiftStateMachine liftStateMachine;
        public static final int MSG_ON = 1;
        public static final int MSG_BUSY_UP = 2;
        public static final int MSG_BUSY_DOWN = 3;
        public static final int MSG_OVER = 4;
        public static final int MSG_OFF = 5;
        public static final int MSG_WAIT = 6;
    
        protected LiftStateMachine(String name) {
            super(name);
            initLiftStateMachine();
        }
    
        protected LiftStateMachine(String name, Looper looper) {
            super(name, looper);
            initLiftStateMachine();
        }
    
        protected LiftStateMachine(String name, Handler handler) {
            super(name, handler);
            initLiftStateMachine();
        }
    
        private State liftOnState = new LiftOnState();
        private State liftWaitState = new LiftWaitForUpOrDownState();
        private State liftBusyState = new LiftBusyState();
        private State liftOverState = new LiftOverState();
        private State liftOffState = new LiftOffState();
    
        private void initLiftStateMachine() {
    
            //添加状态
            addState(liftOnState);
            addState(liftWaitState);
            addState(liftBusyState);
            addState(liftOverState);
            addState(liftOffState);
    
            //调用父类方法设置初始状态
            setInitialState(liftOnState);
            //开启状态机
            start();
        }
    
        //创建状态机
        public static StateMachine makeLiftStateMachine(){
            liftStateMachine = new LiftStateMachine("LiftStateMachine");
            return liftStateMachine;
        }
       }
    

    上述代码中首先定义几种状态响应的常量,用于在各种State中分辨并响应对应的handleMessage消息。其次是实例出电梯的几种状态实例,并在初始化中加入状态树,之后设置原始状态,最后开启状态机进行工作。

  3. 电梯中各种状态的具体

    2.1 待机状态
    
        static class LiftOnState extends State {
    
            @Override
            public void enter() {
                super.enter();
                Log.d(TAG, "LiftOnState --> enter");
    
                Message message = liftStateMachine.obtainMessage();
                message.what = MSG_WAIT;
                liftStateMachine.sendMessage(message);
    
            }
    
            @Override
            public boolean processMessage(Message msg) {
    
                if (msg.what == MSG_WAIT){
                    liftStateMachine.deferMessage(msg);
                    liftStateMachine.transitionTo(liftStateMachine.liftWaitState);
                }
                return super.processMessage(msg);
            }
    
            @Override
            public void exit() {
                super.exit();
                Log.d(TAG, "LiftOnState --> exit");
            }
        }
    

    待机状态中,在进入状态中便发送一个MSG_WAIT消息,由于当前没有切换状态实例,只有在自己的processMessage方法中处理,之后在自己的消息处理逻辑中转移消息,并切换状态实例到等待状态,此时对应电梯门前有人按下电梯按钮,需要等待电梯到达当前楼层。

    2.2 等待状态
    
        static class LiftWaitForUpOrDownState extends State {
    
            @Override
            public void enter() {
                super.enter();
                Log.d(TAG, "LiftWaitForUpOrDownState --> enter");
            }
    
            @Override
            public boolean processMessage(Message msg) {
                switch (msg.what){
                    case MSG_WAIT:
    
                        Log.d(TAG, "LiftWaitForUpOrDownState --> processMessage");
                        Message msg1 = liftStateMachine.obtainMessage();
                        msg1.what = MSG_BUSY_UP;
                        liftStateMachine.sendMessage(msg1);
    
                        //延迟下楼消息
                        Message message = liftStateMachine.obtainMessage();
                        message.what = MSG_BUSY_DOWN;
                        liftStateMachine.sendMessage(message);
                        liftStateMachine.transitionTo(liftStateMachine.liftBusyState);
                        break;
                }
    
            return super.processMessage(msg);
        }

        @Override
        public void exit() {
            super.exit();
            Log.d(TAG, "LiftWaitForUpOrDownState --> exit");
        }
    }

 等待状态逻辑也很简单,主要接收处理MSG_WAIT消息。通过切换状态,进入等待状态的enter方法,之后响应发来的MSG_WAIT消息。此时电梯到达目标楼层,开门等待人员进入并关闭,通过代码可以看出,电梯先向上运行,再向下运行,这时候电梯就要转换状态进入运行状态,此时不能再进行开门和关门等操作。

 2.3 电梯运行状态

    static class LiftBusyState extends State {

        @Override
        public void enter() {
            super.enter();
            Log.d(TAG, "LiftBusyState --> enter");
        }

        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what){
                case MSG_BUSY_UP:
                    Log.d(TAG, "LiftBusyState --> MSG_BUSY_UP");

                    break;
                case MSG_BUSY_DOWN:
                    Log.d(TAG, "LiftBusyState --> MSG_BUSY_DOWN");
                    Message m = liftStateMachine.obtainMessage();
                    m.what = MSG_OVER;
                    liftStateMachine.sendMessage(m);
                    liftStateMachine.transitionTo(liftStateMachine.liftOverState);

            }

            return super.processMessage(msg);
        }

        @Override
        public void exit() {
            super.exit();
            Log.d(TAG, "LiftBusyState --> exit");
        }
    }

人员进入电梯,电梯便运行到达指定楼层,代码中电梯先是到达高楼层,紧接着到达低楼层后,进入到达状态,这是需要请求开门后,人员才可以出入。

2.4 到达状态

    static class LiftOverState extends State {

        @Override
        public void enter() {
            super.enter();
            Log.d(TAG, "LiftOverState --> enter");
        }

        @Override
        public boolean processMessage(Message msg) {

            switch (msg.what){
                case MSG_OVER:
                    Log.d(TAG, "LiftOverState --> MSG_OVER");
                    Message message = liftStateMachine.obtainMessage();
                    message.what = MSG_OFF;
                    liftStateMachine.sendMessage(message);
                    break;
                case MSG_OFF:
                    Log.d(TAG, "LiftOverState --> MSG_OFF");
                    Log.d(TAG, "transitionTo(liftOffState), sendMessage(MSG_OFF)");
                    Message newMsg = liftStateMachine.obtainMessage();
                    newMsg.what = MSG_OFF;
                    liftStateMachine.sendMessage(newMsg);
                    liftStateMachine.transitionTo(liftStateMachine.liftOffState);
                    break;
            }


            return super.processMessage(msg);
        }

          Override
        public void exit() {
            super.exit();
            Log.d(TAG, "LiftOverState --> exit");
        }
    }

 此时电梯到达指定楼层,电梯开门,人员可以安全的出入。代码中,此种状态下响应MSG_OVER消息。但是代码中再响应MSG_OVER没有切换状态,而是在自己响应自己发出的MSG_OFF的消息。稍后我们可以查看log得出此时的MSG_OFF消息是到达状态响应还是已经跳转到停机状态,由停机状态响应。

 2.5 停机状态

    static class LiftOffState extends State {

        @Override
        public void enter() {
            super.enter();
            Log.d(TAG, "LiftOffState --> enter");
        }

        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what){
                case MSG_OFF:
                    Log.d(TAG, "LiftOffState --> MSG_OFF");
                break;
                default:
                    break;
            }
            return super.processMessage(msg);
        }

        @Override
        public void exit() {
            super.exit();
            Log.d(TAG, "LiftOffState --> exit");
        }
    }

电梯停机状态逻辑更是简单,对传来的MSG_OFF关机消息,简单的打印了一个log。当然可以在这里响应其他楼层的电梯等待行为,需要接收MSG_WAIT消息,并将当前状态切换为等待状态,之后电梯才能知道自己要进入运行状态到达指定楼层去接人。
  1. log分析总结

    通过控制台打印的log进行总结

    //电梯开机,便发送消息进入WAIT状态
    LiftState: LiftOnState --> enter
    LiftState: LiftOnState --> exit
    
    //WAIT状态下等待人员进入,之后电梯关门进入运行状态
    LiftWaitForUpOrDownState --> enter
    LiftWaitForUpOrDownState --> processMessage
    LiftWaitForUpOrDownState --> exit
    
    //电梯运行状态,先上后下,最后到达指定楼层,进入到达状态
    LiftBusyState --> enter
    LiftBusyState --> MSG_BUSY_UP
    LiftBusyState --> MSG_BUSY_DOWN
    LiftBusyState --> exit
    
    //到达指定楼层,开门等待人员出入
    LiftOverState --> enter
    LiftOverState --> MSG_OVER
    //这里可以看出,状态没有改变,MSG_OFF仍然在到达状态下响应的,需要切换到停机状态才能真正实现电梯停机
    LiftOverState --> MSG_OFF
    transitionTo(liftOffState), sendMessage(MSG_OFF)
    LiftOverState --> exit
    
    //电梯真正停机,没法响应其他的操作,此时逻辑上只能响应开即进入待机状态,无法进入等待,运行,达到等其他状态
    LiftOffState --> enter
    LiftOffState --> MSG_OFF
    

总结

  • 通过安卓源码的状态机机制,我们可以方便快捷的设计自己的状态模式模型
  • 状态模式的精髓就是各个状态下,各自响应自己能够操作的逻辑。对于越界操作,需要转移状态交予正确的状态来处理。
  • 维护状态的状态树原型也是一种跨越性的思维,其约束了各种状态间之间莫名的转换关系
坚持原创技术分享,您的支持将鼓励我继续创作!