• Android状态机StateMachine使用举例及源码解析


    Android frameworks源码StateMachine使用举例及源码解析

    工作中有一同事说到Android状态机StateMachine。作为一名Android资深工程师,我居然没有听说过StateMachine,因此抓紧时间学习一下。
    StateMachine不是Android SDK中的相关API,其存在于frameworks层源码中的一个Java类。可能因为如此,许多应用层的开发人员并未使用过。
    因此这里我们先说一下StateMachine的使用方式,然后再对源码进行相关介绍。

    • StateMachine使用举例
    • StateMachine原理学习

    一、StateMachine使用举例

    StateMachine 处于Android frameworks层源码frameworks/base/core/java/com/android/internal/util路径下。应用层若要使用StateMachine需将对应路径下的三个类拷贝到自己的工程目录下。
    这三个类分别为:StateMachine.javaStateIState

    下边是使用的代码举例,这个例子我也是网络上找的(读懂StateMachine源码后,我对这个例子进行了一些简单更改,以下为更改后的案例):

    主要分以下几个部分来说明:

    • PersonStateMachine.java案例代码
    • PersonStateMachine 使用
    • 案例的简单说明
    • 案例源码下载

    1.1、PersonStateMachine.java

    创建PersonStateMachine继承StateMachine类。
    创建四种状态,四种状态均继承自State

    • 默认状态 BoringState
    • 工作状态 WorkState
    • 吃饭状态 EatState
    • 睡觉状态 SleepState

    定义了状态转换的四种消息类型:

    • 唤醒消息 MSG_WAKEUP
    • 困乏消息 MSG_TIRED
    • 饿了消息 MSG_HUNGRY
    • 状态机停止消息 MSG_HALTING

    下面来看完整的案例代码:

    public class PersonStateMachine extends StateMachine {
    
        private static final String TAG = "MachineTest";
    
        //设置状态改变标志常量
        public static final int MSG_WAKEUP = 1; // 消息:醒
        public static final int MSG_TIRED = 2; // 消息:困
        public static final int MSG_HUNGRY = 3; // 消息:饿
        private static final int MSG_HALTING = 4; // 状态机暂停消息
    
        //创建状态
        private State mBoringState = new BoringState();// 默认状态
        private State mWorkState = new WorkState(); // 工作
        private State mEatState = new EatState(); // 吃
        private State mSleepState = new SleepState(); // 睡
    
        /**
         * 构造方法
         *
         * @param name
         */
        PersonStateMachine(String name) {
            super(name);
            //加入状态,初始化状态
            addState(mBoringState, null);
            addState(mSleepState, mBoringState);
            addState(mWorkState, mBoringState);
            addState(mEatState, mBoringState);
    
            // sleep状态为初始状态
            setInitialState(mSleepState);
        }
    
        /**
         * @return 创建启动person 状态机
         */
        public static PersonStateMachine makePerson() {
            PersonStateMachine person = new PersonStateMachine("Person");
            person.start();
            return person;
        }
    
    
        @Override
        protected void onHalting() {
            synchronized (this) {
                this.notifyAll();
            }
        }
    
    
        /**
         * 定义状态:无聊
         */
        class BoringState extends State {
            @Override
            public void enter() {
                Log.e(TAG, "############ enter Boring ############");
            }
    
            @Override
            public void exit() {
                Log.e(TAG, "############ exit Boring ############");
            }
    
            @Override
            public boolean processMessage(Message msg) {
                Log.e(TAG, "BoringState  processMessage.....");
                return true;
            }
        }
    
        /**
         * 定义状态:睡觉
         */
        class SleepState extends State {
            @Override
            public void enter() {
                Log.e(TAG, "############ enter Sleep ############");
            }
    
            @Override
            public void exit() {
                Log.e(TAG, "############ exit Sleep ############");
            }
    
            @Override
            public boolean processMessage(Message msg) {
                Log.e(TAG, "SleepState  processMessage.....");
                switch (msg.what) {
                    // 收到清醒信号
                    case MSG_WAKEUP:
                        Log.e(TAG, "SleepState  MSG_WAKEUP");
                        // 进入工作状态
                        transitionTo(mWorkState);
                        //...
                        //...
                        //发送饿了信号...
                        sendMessage(obtainMessage(MSG_HUNGRY));
                        break;
                    case MSG_HALTING:
                        Log.e(TAG, "SleepState  MSG_HALTING");
    
                        // 转化到暂停状态
                        transitionToHaltingState();
                        break;
                    default:
                        return false;
                }
                return true;
            }
        }
    
    
        /**
         * 定义状态:工作
         */
        class WorkState extends State {
            @Override
            public void enter() {
                Log.e(TAG, "############ enter Work ############");
            }
    
            @Override
            public void exit() {
                Log.e(TAG, "############ exit Work ############");
            }
    
            @Override
            public boolean processMessage(Message msg) {
                Log.e(TAG, "WorkState  processMessage.....");
                switch (msg.what) {
                    // 收到 饿了 信号
                    case MSG_HUNGRY:
                        Log.e(TAG, "WorkState  MSG_HUNGRY");
                        // 吃饭状态
                        transitionTo(mEatState);
                        //...
                        //...
                        // 发送累了信号...
                        sendMessage(obtainMessage(MSG_TIRED));
                        break;
                    default:
                        return false;
                }
                return true;
            }
        }
    
        /**
         * 定义状态:吃
         */
        class EatState extends State {
            @Override
            public void enter() {
                Log.e(TAG, "############ enter Eat ############");
            }
    
            @Override
            public void exit() {
                Log.e(TAG, "############ exit Eat ############");
            }
    
            @Override
            public boolean processMessage(Message msg) {
                Log.e(TAG, "EatState  processMessage.....");
                switch (msg.what) {
                    // 收到 困了 信号
                    case MSG_TIRED:
                        Log.e(TAG, "EatState  MSG_TIRED");
                        // 睡觉
                        transitionTo(mSleepState);
                        //...
                        //...
                        // 发出结束信号...
                        sendMessage(obtainMessage(MSG_HALTING));
                        break;
                    default:
                        return false;
                }
                return true;
            }
    
        }
    }
    

    1.2、PersonStateMachine 使用

    // 获取 状态机引用
    PersonStateMachine personStateMachine = PersonStateMachine.makePerson();
    // 初始状态为SleepState,发送消息MSG_WAKEUP
    personStateMachine.sendMessage(PersonStateMachine.MSG_WAKEUP);
    
    • SleepState状态收到MSG_WAKEUP消息后,会执行对应状态的processMessage方法
    • SleepState类中processMessage方法收到MSG_WAKEUP消息后,执行transitionTo(mWorkState)方法,完成状态转换。转换到WorkState状态。

    1.3、案例的简单说明

    几种状态的依赖关系如下:
    在这里插入图片描述

    构造方法中,添加所有状态,并设置初始状态:

    PersonStateMachine(String name) {
        super(name);
        //加入状态,初始化状态
        addState(mBoringState, null);
        addState(mSleepState, mBoringState);
        addState(mWorkState, mBoringState);
        addState(mEatState, mBoringState);
        
        // sleep状态为初始状态
        setInitialState(mSleepState);
    }
    

    通过以下方法,创建并启动状态机:

    public static PersonStateMachine makePerson() {
        PersonStateMachine person = new PersonStateMachine("Person");
        person.start();
        return person;
    }
    

    1.4、案例源码下载

    Android_StateMachine案例地址

    二、实现原理学习

    StateMachine中,开启了一个线程HandlerThread,其对应的Handler为SmHandler。因此上文案例中对应状态的 processMessage(Message msg)方法,均在HandlerThread线程中执行。

    2.1、首先从StateMachine的构造方法说起,对应的代码如下:

    protected StateMachine(String name) {
        // 创建 HandlerThread
        mSmThread = new HandlerThread(name);
        mSmThread.start();
        // 获取HandlerThread对应的Looper
        Looper looper = mSmThread.getLooper();
        // 初始化 StateMachine
        initStateMachine(name, looper);
    }
    
    • StateMachine的构造方法中,创建并启动了一个线程HandlerThread
    • initStateMachine方法中,创建了HandlerThread线程对应的Handler SmHandler
    private void initStateMachine(String name, Looper looper) {
        mName = name;
        mSmHandler = new SmHandler(looper, this);
    }
    
    • SmHandler构造方法中,向状态机中添加了两个状态:一个状态为状态机的暂停状态mHaltingState、一个状态为状态机的退出状态mQuittingState
    private SmHandler(Looper looper, StateMachine sm) {
        super(looper);
        mSm = sm;
    
        // 添加状态:暂停 和 退出
        // 这两个状态 无父状态
        addState(mHaltingState, null);
        addState(mQuittingState, null);
    }
    
    • mHaltingState状态,顾名思义让状态机暂停,其对应的processMessage(Message msg)方法,返回值为true,将消息消费掉,但不处理消息。从而使状态机状态停顿到mHaltingState状态
    • mQuittingState状态,若进入该状态, 状态机将退出。HandlerThread线程对应的Looper将退出,HandlerThread线程会被销毁,所有加入到状态机的状态被清空。

    2.2、状态机的start() 方法

    状态机的初始化说完,下边来说状态机的启动方法start()

    public void start() {
        // mSmHandler can be null if the state machine has quit.
        SmHandler smh = mSmHandler;
        // StateMachine 未进行初始化,为什么不抛出一个异常
        if (smh == null) {
            return;
        }
        // 完成状态机建设
       smh.completeConstruction();
    }
    
    • 从以上代码可以看到,其中只有一个方法completeConstruction(),用于完成状态机的建设。
    private final void completeConstruction() {
        int maxDepth = 0;
        // 循环判断所有状态,看看哪一个链最长,得出深度
        for (StateInfo si : mStateInfoHashMap.values()) {
            int depth = 0;
            for (StateInfo i = si; i != null; depth++) {
                i = i.parentStateInfo;
            }
            if (maxDepth < depth) {
                maxDepth = depth;
            }
        }
        // 状态堆栈
        mStateStack = new StateInfo[maxDepth];
        // 临时状态堆栈
        mTempStateStack = new StateInfo[maxDepth];
        // 初始化堆栈
        setupInitialStateStack();
    
        // 发送初始化完成的消息(消息放入到队列的最前边)
        sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
    }
    
    • maxDepth是状态机中,最长依赖链的长度。
    • mStateStackmTempStateStack为两个用数组实现的堆栈。这两个堆栈的最大长度,即为maxDepth。其用来存储当前活跃状态当前活跃状态的父状态、父父状态、...等
    • setupInitialStateStack();完成状态的初始化,将当前的活跃状态放入到mStateStack堆栈中。

    下边来具体说setupInitialStateStack();方法中,如何完成栈的初始化。

    private final void setupInitialStateStack() {
        // 获取初始状态信息
        StateInfo curStateInfo = mStateInfoHashMap.get(mInitialState);
        //
        for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
            // 初始状态 放入临时堆栈
            mTempStateStack[mTempStateStackCount] = curStateInfo;
            // 当前状态的 所有父状态 一级级放入堆栈
            curStateInfo = curStateInfo.parentStateInfo;
        }
    
        // 清空 状态堆栈
        // Empty the StateStack
        mStateStackTopIndex = -1;
        // 临时堆栈 换到 状态堆栈
        moveTempStateStackToStateStack();
    }
    
    • 拿案例中状态来举例,将初始化状态放入 mTempStateStack堆栈中
    • 初始化状态父状态父父状态父父父状态... 都一一放入到mTempStateStack堆栈中

    enter description here

    • 然后moveTempStateStackToStateStack()方法中,mTempStateStack出栈,mStateStack入栈,将所有状态信息导入到mStateStack堆栈,并清空mTempStateStack堆栈。

    enter description here

    到这里,初始化基本完成,但我们还落下一部分代码没有说:

    // 发送初始化完成的消息(消息放入到队列的最前边)
    sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
    
    • 发送一个初始化完成的消息到SmHandler当中。

    下边来看一下SmHandlerhandleMessage(Message msg)方法:

    public final void handleMessage(Message msg) {
    
        // 处理消息
        if (!mHasQuit) {
            // 保存传入的消息
            mMsg = msg;
            State msgProcessedState = null;
            // 已完成初始化
            if (mIsConstructionCompleted) {
    		// ..
            }
            // 接收到 初始化完成的消息
            else if (!mIsConstructionCompleted
                    && (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
                /** Initial one time path. */
                // 初始化完成
                mIsConstructionCompleted = true;
                // 调用堆栈中状态的enter方法,并将堆栈中的状态设置为活跃状态
                invokeEnterMethods(0);
            } else {
    		// ..
            }
            // 执行Transition
            performTransitions(msgProcessedState, msg);
        }
    }
    
    • 接收到初始化完成的消息后mIsConstructionCompleted = true;对应的标志位变过来
    • 执行 invokeEnterMethods方法将mStateStack堆栈中的所有状态设置为活跃状态,并由父—>子的顺序,执行堆栈中状态的enter()方法
    • performTransitions(msgProcessedState, msg);在start()时,其中的内容全部不执行,因此先不介绍。

    invokeEnterMethods方法的方法体如下:

    private final void invokeEnterMethods(int stateStackEnteringIndex) {
        for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
            if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
            mStateStack[i].state.enter();
            mStateStack[i].active = true;
        }
    }
    
    • 可以看到,其将mStateStack堆栈中的所有状态设置为活跃状态,并由父—>子的顺序,执行堆栈中状态的enter()方法

    到此start()完成,最终mStateStack堆栈状态,也如上图所示。

    2.3、状态转化

    还是拿案例中的代码举例:

    // 获取 状态机引用
    PersonStateMachine personStateMachine = PersonStateMachine.makePerson();
    // 初始状态为SleepState,发送消息MSG_WAKEUP
    personStateMachine.sendMessage(PersonStateMachine.MSG_WAKEUP);
    
    • 通过调用sendMessage(PersonStateMachine.MSG_WAKEUP);方法,向SmHandler中发送一个消息,来触发状态转化。
    • 可以说 sendMessage(PersonStateMachine.MSG_WAKEUP);消息,为状态转化的导火索。

    下边,再次看一下SmHandlerhandleMessage(Message msg)方法:

    public final void handleMessage(Message msg) {
        // 处理消息
        if (!mHasQuit) {
            // 保存传入的消息
            mMsg = msg;
            State msgProcessedState = null;
            // 已完成初始化
            if (mIsConstructionCompleted) {
                // 处理消息的状态
                msgProcessedState = processMsg(msg);
            }
            // 接收到 初始化完成的消息
            else if (!mIsConstructionCompleted
                    && (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
                // 初始化完成
                mIsConstructionCompleted = true;
                // 调用堆栈中状态的enter方法,并将堆栈中的状态设置为活跃状态
                invokeEnterMethods(0);
            } else {
                throw new RuntimeException("StateMachine.handleMessage: "
                        + "The start method not called, received msg: " + msg);
            }
            // 执行Transition
            performTransitions(msgProcessedState, msg);
        }
    }
    
    • 因为初始化已经完成,代码会直接走到processMsg(msg);方法中。

    我们来看processMsg(msg);方法:

    private final State processMsg(Message msg) {
        // 堆栈中找到当前状态
        StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
        // 是否为退出消息
        if (isQuit(msg)) {
            // 转化为退出状态
            transitionTo(mQuittingState);
        } else {
            // 状态返回true 则是可处理此状态
            // 状态返回false 则不可以处理
            while (!curStateInfo.state.processMessage(msg)) {
                // 当前状态的父状态
                curStateInfo = curStateInfo.parentStateInfo;
                // 父状态未null
                if (curStateInfo == null) {
                    // 回调到未处理消息方法中
                    mSm.unhandledMessage(msg);
                    break;
                }
            }
        }
        // 消息处理后,返回当前状态信息
        // 如果消息不处理,则返回其父状态处理,返回处理消息的父状态
        return (curStateInfo != null) ? curStateInfo.state : null;
    }
    
    • 代码会直接走到while (!curStateInfo.state.processMessage(msg))
      执行mStateStack堆栈中,最上层状态的 processMessage(msg)方法。案例中这个状态为SleepState
    • 这里
      如果mStateStack堆栈中状态的processMessage(msg)方法返回true,则表示其消费掉了这个消息;
      如果其返回false,则表示不消费此消息,那么该消息将继续向其父状态进行传递;
    • 最终将返回,消费掉该消息的状态。

    这里,堆栈对上层的状态为SleepState。所以我们看一下其对应的processMessage(msg)方法。

    public boolean processMessage(Message msg) {
        switch (msg.what) {
            // 收到清醒信号
            case MSG_WAKEUP:
                // 进入工作状态
                transitionTo(mWorkState);
                //...
                //...
                //发送饿了信号...
                sendMessage(obtainMessage(MSG_HUNGRY));
                break;
            case MSG_HALTING:
    		// ...
                break;
            default:
                return false;
        }
        return true;
    }
    
    • 在SleepState状态的processMessage(Message msg)方法中,其收到MSG_WAKEUP消息后,会调用transitionTo(mWorkState);方法,将目标状态设置为mWorkState

    我们看一下transitionTo(mWorkState);方法:

    private final void transitionTo(IState destState) {
        mDestState = (State) destState;
    }
    
    • 可以看到,transitionTo(IState destState)方法,只是一个简单的状态赋值。

    下边我们回到SmHandlerhandleMessage(Message msg)方法:

    • 代码会执行到SmHandler.handleMessage(Message msg)performTransitions(msgProcessedState, msg);方法之中。
    • 而这里我们传入的参数msgProcessedStatemSleepState
    private void performTransitions(State msgProcessedState, Message msg) {
        // 当前状态
        State orgState = mStateStack[mStateStackTopIndex].state;
    	// ...
        // 目标状态
        State destState = mDestState;
        if (destState != null) {
            while (true) {
                // 目标状态 放入temp 堆栈
                // 目标状态的 父状态 作为参数 传入下一级
                StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                // commonStateInfo 状态的子状态全部退栈
                invokeExitMethods(commonStateInfo);
                // 目标状态入栈
                int stateStackEnteringIndex = moveTempStateStackToStateStack();
                // 入栈状态 活跃
                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;
        }
    	// ...
    }
    
    • 以上方法中 传入的参数msgProcessedStatemSleepState
    • 方法中destState目标状态为 mWorkState

    此时此刻performTransitions(State msgProcessedState, Message msg)方法中内容的执行示意图如下:

    A、目标状态放入到mTempStateStack队列中
    // 目标状态 放入temp 堆栈
    // 目标状态的 父状态 作为参数 传入下一级
    StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
    
    • 1、将WorkState状态放入到mTempStateStack堆栈中
    • 2、将WorkState状态的非活跃父状态一一入mTempStateStack堆栈
    • 3、因为WorkState状态的父状态为BoringState,是活跃状态,因此只将WorkState放入到mTempStateStack堆栈中
    • 4、返回活跃的父状态BoringState

    以上代码的执行示意图如下:
    enter description here

    B、commonStateInfo状态在mStateStack堆栈中的子状态退堆栈

    commonStateInfosetupTempStateStackWithStatesToEnter(destState);方法的返回参数。这里是BoringState

    // commonStateInfo 状态的子状态全部退栈
    invokeExitMethods(commonStateInfo);
    
    • 1、BoringState作为参数传入到invokeExitMethods(commonStateInfo);方法中
    • 2、其方法内容为,将BoringState状态的全部子状态退堆栈

    以上代码的执行示意图如下:
    在这里插入图片描述

    C、mTempStateStack全部状态出堆栈,mStateStack入堆栈
    // 目标状态入栈
    int stateStackEnteringIndex = moveTempStateStackToStateStack();
    // 入栈状态 活跃
    invokeEnterMethods(stateStackEnteringIndex);
    
    • moveTempStateStackToStateStack方法中:mTempStateStack全部状态出堆栈,mStateStack入堆栈
    • invokeEnterMethods(stateStackEnteringIndex);方法中,将新加入的状态设置为活跃状态;并调用其对应的enter()方法。

    最终的堆栈状态为:

    在这里插入图片描述

    到此StateMachine的源码讲解完成。
    感兴趣的同学,还是自己读一遍源码吧,希望我的这篇文章可以为你的源码阅读提供一些帮助。

    ========== THE END ==========

    wx_gzh.jpg

  • 相关阅读:
    Window7下手动编译最新版的PCL库
    C和C++中的异常处理
    队列之卡片游戏
    C/C++笔记
    最真的梦——文/林清玄
    库函数与系统调用的区别
    hdu2653之BFS
    使用ffmpeg+crtmpserver搭建文件的伪直播
    ZOJ3705:Applications
    c_c++基础问题(平时读书时笔记)
  • 原文地址:https://www.cnblogs.com/xiaxveliang/p/12395670.html
Copyright © 2020-2023  润新知