iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android StateMachine使用举例及源码解析
  • 883
分享到

Android StateMachine使用举例及源码解析

源码Android 2022-06-06 12:06:50 883人浏览 薄情痞子
摘要

Android frameworks源码StateMachine使用举例及源码解析 工作中有一同事说到Android状态机StateMachin

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.java
State
IState

下边是使用的代码举例,这个例子我也是网络上找的(读懂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(); // 睡
    
    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;
    }
    @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
是状态机中,最长依赖链的长度。
mStateStack
mTempStateStack
为两个用数组实现的堆栈。这两个堆栈的最大长度,即为
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
当中。

下边来看一下

SmHandler
handleMessage(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)) {
            
            // 初始化完成
            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);
消息,为状态转化的导火索。

下边,再次看一下

SmHandler
handleMessage(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)
方法,只是一个简单的状态赋值。

下边我们回到

SmHandler
handleMessage(Message msg)
方法:

代码会执行到
SmHandler.handleMessage(Message msg)
performTransitions(msgProcessedState, msg);
方法之中。 而这里我们传入的参数
msgProcessedState
mSleepState
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;
    }
	// ...
}
以上方法中 传入的参数
msgProcessedState
mSleepState
方法中
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
堆栈中的子状态退堆栈

commonStateInfo
setupTempStateStackWithStatesToEnter(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的源码讲解完成。
感兴趣的同学,还是自己读一遍源码吧,希望我的这篇文章可以为你的源码阅读提供一些帮助。


作者:xiaxl


--结束END--

本文标题: Android StateMachine使用举例及源码解析

本文链接: https://www.lsjlt.com/news/28803.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作