Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

android/4.4w_r1/xref/frameworks/base/core/java/com/android/internal/util/StateMachine.java 源码实现学习 与 StateMachine使用案例

Notifications You must be signed in to change notification settings

AndroidHighQualityCodeStudy/Android_-StateMachine

Repository files navigation

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

About

android/4.4w_r1/xref/frameworks/base/core/java/com/android/internal/util/StateMachine.java 源码实现学习 与 StateMachine使用案例

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

AltStyle によって変換されたページ (->オリジナル) /