Android状态机StateMachine使用举例及源码解析
xiaxveliang 人气:0
## 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
下面来看完整的案例代码:
```java
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 使用
```java
// 获取 状态机引用
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、案例的简单说明
几种状态的依赖关系如下:
![在这里插入图片描述](https://upload-images.jianshu.io/upload_images/5969042-09b072f7bc91cd5c?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
构造方法中,添加所有状态,并设置初始状态:
```java
PersonStateMachine(String name) {
super(name);
//加入状态,初始化状态
addState(mBoringState, null);
addState(mSleepState, mBoringState);
addState(mWorkState, mBoringState);
addState(mEatState, mBoringState);
// sleep状态为初始状态
setInitialState(mSleepState);
}
```
通过以下方法,创建并启动状态机:
```java
public static PersonStateMachine makePerson() {
PersonStateMachine person = new PersonStateMachine("Person");
person.start();
return person;
}
```
#### 1.4、案例源码下载
[Android_StateMachine案例地址](https://github.com/AndroidHighQualityCodeStudy/Android_-StateMachine)
### 二、实现原理学习
在 `StateMachine`中,开启了一个线程`HandlerThread`,其对应的Handler为`SmHandler`。因此上文案例中对应状态的 `processMessage(Message msg)`方法,均在`HandlerThread`线程中执行。
#### 2.1、首先从`StateMachine`的构造方法说起,对应的代码如下:
```java
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`
```java
private void initStateMachine(String name, Looper looper) {
mName = name;
mSmHandler = new SmHandler(looper, this);
}
```
+ `SmHandler`构造方法中,向状态机中添加了两个状态:一个状态为状态机的`暂停状态mHaltingState`、一个状态为状态机的`退出状态mQuittingState`
```java
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()`
```java
public void start() {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
// StateMachine 未进行初始化,为什么不抛出一个异常
if (smh == null) {
return;
}
// 完成状态机建设
smh.completeConstruction();
}
```
+ 从以上代码可以看到,其中只有一个方法`completeConstruction()`,用于完成状态机的建设。
```java
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();`方法中,如何完成栈的初始化。
```java
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](https://upload-images.jianshu.io/upload_images/5969042-3ce735290078240f?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
+ 然后moveTempStateStackToStateStack()方法中,`mTempStateStack`出栈,`mStateStack`入栈,将所有状态信息导入到`mStateStack`堆栈,并清空`mTempStateStack`堆栈。
![enter description here](https://upload-images.jianshu.io/upload_images/5969042-4c0d1e7349a4c74c?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
到这里,初始化基本完成,但我们还落下一部分代码没有说:
```java
// 发送初始化完成的消息(消息放入到队列的最前边)
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
```
+ 发送一个初始化完成的消息到`SmHandler`当中。
下边来看一下`SmHandler`的`handleMessage(Message msg)`方法:
```java
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`方法的方法体如下:
```java
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、状态转化
还是拿案例中的代码举例:
```java
// 获取 状态机引用
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)`方法:
```java
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);`方法:
```java
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)`方法。
```java
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);`方法:
```java
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`。
```java
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队列中
```java
// 目标状态 放入temp 堆栈
// 目标状态的 父状态 作为参数 传入下一级
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
```
+ 1、将`WorkState`状态放入到`mTempStateStack`堆栈中
+ 2、将`WorkState`状态的`非活跃父状态`一一入`mTempStateStack`堆栈
+ 3、因为`WorkState`状态的父状态为`BoringState`,是活跃状态,因此只将`WorkState`放入到`mTempStateStack`堆栈中
+ 4、返回活跃的父状态`BoringState`
以上代码的执行示意图如下:
![enter description here](https://upload-images.jianshu.io/upload_images/5969042-000ede026b361c7e?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
##### B、`commonStateInfo`状态在`mStateStack`堆栈中的子状态退堆栈
`commonStateInfo`为`setupTempStateStackWithStatesToEnter(destState);`方法的返回参数。这里是`BoringState`
```java
// commonStateInfo 状态的子状态全部退栈
invokeExitMethods(commonStateInfo);
```
+ 1、`BoringState`作为参数传入到`invokeExitMethods(commonStateInfo);`方法中
+ 2、其方法内容为,将`BoringState`状态的`全部子状态退堆栈`
以上代码的执行示意图如下:
![在这里插入图片描述](https://upload-images.jianshu.io/upload_images/5969042-7d84cec802856710?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
##### C、`mTempStateStack`全部状态出堆栈,`mStateStack`入堆栈
```java
// 目标状态入栈
int stateStackEnteringIndex = moveTempStateStackToStateStack();
// 入栈状态 活跃
invokeEnterMethods(stateStackEnteringIndex);
```
+ `moveTempStateStackToStateStack`方法中:`mTempStateStack`全部状态出堆栈,`mStateStack`入堆栈
+ invokeEnterMethods(stateStackEnteringIndex);方法中,将新加入的状态设置为`活跃状态`;并调用其对应的`enter()`方法。
最终的堆栈状态为:
![在这里插入图片描述](https://upload-images.jianshu.io/upload_images/5969042-4ad436e272d77fdf?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
到此StateMachine的源码讲解完成。
感兴趣的同学,还是自己读一遍源码吧,希望我的这篇文章可以为你的源码阅读提供一些帮助。
## ========== THE END ==========
![wx_gzh.jpg](https://upload-images.jianshu.io/upload_images/5969042-09fd25b1ec5b86fe.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
加载全部内容