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.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(); // 睡 /** * 构造方法 * * @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、案例的简单说明
几种状态的依赖关系如下:
构造方法中,添加所有状态,并设置初始状态:
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
线程对应的HandlerSmHandler
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堆栈中
- 然后moveTempStateStackToStateStack()方法中,
mTempStateStack
出栈,mStateStack
入栈,将所有状态信息导入到mStateStack
堆栈,并清空mTempStateStack
堆栈。
到这里,初始化基本完成,但我们还落下一部分代码没有说:
// 发送初始化完成的消息(消息放入到队列的最前边)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)) { /** 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);
消息,为状态转化的导火索。
下边,再次看一下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
以上代码的执行示意图如下:
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的源码讲解完成。
感兴趣的同学,还是自己读一遍源码吧,希望我的这篇文章可以为你的源码阅读提供一些帮助。
========== THE END ==========
更多相关文章
- Android中杀进程的几种方法 (1) - killBackgroundProcesses
- Android客户端和服务器端数据交互的第四种方法
- Android选项卡的几种实现方法
- MTK 添加宏控方法
- Android AsyncTask onProgressUpdate 方法的些许研究
- Android Studio中genymotion安装方法
- Android使用HttpClient方法和易错问题
- Android开机自启动程序设置及控制方法思路浅谈
- android获取当前运行Activity名字的方法