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_WAKEUPpersonStateMachine.sendMessage(PersonStateMachine.MSG_WAKEUP);
  • SleepState状态收到MSG_WAKEUP消息后,会执行对应状态的processMessage方法
  • SleepState类中processMessage方法收到MSG_WAKEUP消息后,执行transitionTo(mWorkState)方法,完成状态转换。转换到WorkState状态。

1.3、案例的简单说明

几种状态的依赖关系如下:
Android状态机StateMachine使用举例及源码解析_第1张图片

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

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堆栈中

Android状态机StateMachine使用举例及源码解析_第2张图片

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

Android状态机StateMachine使用举例及源码解析_第3张图片

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

// 发送初始化完成的消息(消息放入到队列的最前边)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_WAKEUPpersonStateMachine.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

以上代码的执行示意图如下:
Android状态机StateMachine使用举例及源码解析_第4张图片

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

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

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

以上代码的执行示意图如下:
Android状态机StateMachine使用举例及源码解析_第5张图片

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

最终的堆栈状态为:

Android状态机StateMachine使用举例及源码解析_第6张图片

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

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

Android状态机StateMachine使用举例及源码解析_第7张图片

更多相关文章

  1. Android中杀进程的几种方法 (1) - killBackgroundProcesses
  2. Android客户端和服务器端数据交互的第四种方法
  3. Android选项卡的几种实现方法
  4. MTK 添加宏控方法
  5. Android AsyncTask onProgressUpdate 方法的些许研究
  6. Android Studio中genymotion安装方法
  7. Android使用HttpClient方法和易错问题
  8. Android开机自启动程序设置及控制方法思路浅谈
  9. android获取当前运行Activity名字的方法

随机推荐

  1. Android的蓝牙实例代码中找不到REQUEST_E
  2. [Android NDK]Android JNI开发例子 ---2
  3. 【Android】进入应用和切换界面时闪屏的
  4. 总结Android中的Info系列类
  5. Android简单模糊背景和圆形ImageView
  6. android studio GC overhead limit excee
  7. Android自定义Toast样式和时间
  8. Android初学路上会遇到的瓶颈
  9. Android(安卓)viewpager里面嵌套使用list
  10. Android中的PCM设备