Android 中 StateMachine 机制
分层处理消息的状态机,能够分层排列 在不同的状态下,收到不同的消息时,在不同的阶段做出不同的响应。 StateMachine 处于 Android frameworks 层源码 frameworks/base/core/java/com/android/internal/util 路径下,将此路经下的三个类 拷贝到自己的工程里,StateMachine.java,State.java,IState.java(在最后面我粘贴了这三 个代码,直接复制就可以)。
• 在 Mainactivity 里面
// 获取 状态机引用
PersonStateMachine personStateMachine = PersonStateMachine.makePerson();
// 初始状态为SleepState,发送消息MSG_WAKEUP
personStateMachine.sendMessage(PersonStateMachine.MSG_WAKEUP);
已经设置了初始状态为 SleepState,初始状态收到 MSG_WAKEUP 消息,会执行相对应的 processMessage()方法。
• 继承 StateMachine,构造函数是 protect 类型,不能实例化,要通过继承子类进行初始化 操作。
package com.xiaxl.demo;
import android.os.Message;
import android.util.Log;
import com.xiaxl.demo.statemachine.State;
import com.xiaxl.demo.statemachine.StateMachine;
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; //停
protected PersonStateMachine(String name) {
super(name);
}
@Override
protected void onHalting() {
Log.e(TAG, "PersonStateMachine halting");
synchronized (this) {
this.notifyAll();
}
}
}
• 通过 addState 方法构造状态层次结构(树形结构,创建层级),各种状态都要继承 State 类,实现自己相应的业务逻辑。(有零个或者一个父状态)
/创建状态
private State mBoringState = new BoringState();// 默认状态
private State mWorkState = new WorkState(); // 工作
private State mEatState = new EatState(); // 吃
private State mSleepState = new SleepState(); // 睡
//构造方法
protected PersonStateMachine(String name) {
super(name);
Log.e(TAG, "PersonStateMachine");
//加入状态,初始化状态
addState(mBoringState, null);
addState(mSleepState, mBoringState);
addState(mWorkState, mBoringState);
addState(mEatState, mBoringState);
}
/**
* 定义状态:无聊
*/
class BoringState extends State {
@Override
public void enter() {
Log.e(TAG, "BoringState 进入 无聊");
}
@Override
public void exit() {
Log.e(TAG, "BoringState 退出 无聊");
}
@Override
public boolean processMessage(Message msg) {
Log.e(TAG, "无聊 processMessage.....");
return true;
}
}
//继承State.java
/**
* 定义状态:工作
*/
class WorkState extends State {
@Override
//状态机进程时执行
public void enter() {
Log.e(TAG, "WorkState 进入 工作");
}
//退出时执行,释放资源
@Override
public void exit() {
Log.e(TAG, "WorkState 退出 工作");
}
//消息处理。
@Override
public boolean processMessage(Message msg) {
Log.e(TAG, "工作 processMessage.....");
switch (msg.what) {
// 收到 饿了 信号
case MSG_HUNGRY:
Log.e(TAG, "工作 饿");
// 吃饭状态
//暂时将当前消息保存到消息队列的顶端
deferMessage(msg);
//执行一次状态的转换
transitionTo(mEatState);
// 发送信号
sendMessage(obtainMessage(MSG_TIRED));
break;
default:
return false;
}
return true;
}
/**
* 定义状态:睡觉
*/
class SleepState extends State {
@Override
public void enter() {
Log.e(TAG, "SleepState 进入 睡觉");
}
@Override
public void exit() {
Log.e(TAG, "SleepState 退出 睡觉");
}
@Override
public boolean processMessage(Message msg) {
Log.e(TAG, "睡觉 processMessage.....");
switch (msg.what) {
// 收到清醒信号
case MSG_WAKEUP:
Log.e(TAG, "睡觉 醒");
// 进入工作状态
deferMessage(msg);
transitionTo(mWorkState);
//
//发送饿了信号...
sendMessage(obtainMessage(MSG_HUNGRY));
break;
case MSG_HALTING:
Log.e(TAG, "睡觉 停");
// 停止
transitionToHaltingState();//过渡到停止状态
break;
default:
return false;
}
return true;
}
}
}
• 通过 setInitialDtate 设置初始状态
// sleep状态为初始状态
setInitialState(mSleepState);
• 调用 start 方法启动状态机。
public static PersonStateMachine makePerson() {
Log.e(TAG, "PersonStateMachine makePerson");
PersonStateMachine person = new PersonStateMachine("Person");
person.start();
return person;
}
• 开始从初始状态最顶层的父状态开始逐层调用 enter 方法,在所有的 message 执行前被调 用。例如:无聊 -> 睡觉,先执行无聊的 enter,再执行睡觉的 enter,最后 message 会被 送到当前的状态执行
此时:无聊(父状态)------> 睡觉
• 当状态机收到 message(MainActivity.java 传来的 MSG_WAKEUP)之后,当前状态的 processMessage()会被调用,也就是睡觉的 processMessage()会被调用(因为 switch-case 匹配上了),然后通过 transitionTo()跳转到新的状态(就要先退出,才能再转换成新 的状态)。
注意:
状态的转换会导致当前状态的退出,和新状态的进入,当从当前状态退出时,会逐 层向上调用父状态的退出 exit 函数,但注意,这种逐层调用,会在当前状态和目标 状态的共同父状态处不再执行 exit(),如果前状态和目标状态的不存在共同的父状 态,则彻底退出当前状态的所有父状态,并进入新状态。
此时:睡觉 -----> 工作
在这里注意一下: deferMessage(msg); 因为有了这个东西,将当前的消息(也就是传过来的 MSG_WAKEUP)保存到消息 队列的顶端,当进入工作的状态之后,swich-case 的就是消息队列顶端的 message,但是匹配不上,那么就会 default,返回 false。 【deferMessage 方法会将该消息保存在一个延迟队列中,这时并不发送出去,而 是会在下一次状态转变的时候(例如从 A 状态变为 B 状态),将延迟队列中的所有 消息放在消息队列的最前面。这些消息就会在 B 状态作为当前状态时被处理。 】
• 如果当前状态执行完 processMessage()方法返回了 false,也就是对当前消息 NOT_HANDLED(不处理当前消息), 那么就会向上调用这个状态的父状态执行方法。 (在 addState 的时候,工作的父状态是无聊)(一般终有一个状态能够处理消息(返回 true),如果最顶端的父类都没有处理,会被记录到 unhandledMessage()里面做最后的 处理,一般是丢掉,也可以自己定义最后的处理函数)
当返回了 true 之后,就是可以处理 message 了,然后根据睡觉状态发送过来的 message 进行 switch-case,可以匹配之后,就能继续往下进行了。
• 当所有进程结束之后,最后,发送一个结束的信号,调用 transitionToHaltingState()方 法,StateMachine 会跳转到内部的 HaltingState 和调用 halting,如果 StateMachine 正 常的退出,可以调用 quit 或者 abort,调用 quit 会退出当前状态和他的父状态,调用 onQuiting 之后退出 Thread 和 Loopers。
// 发出结束信号...
sendMessage(obtainMessage(MSG_HALTING));
case MSG_HALTING:
Log.e(TAG, "睡觉 停");
// 停止
transitionToHaltingState();//过渡到停止状态
break;
start()方法用于启动状态机,transitionTo()方法用于设置新状态, performTransitions()是去切换新状态,sendMessage()方法用于向 mSmHandler 发送消息,mSmHandler 的 handleMessage 方法会把消息抛个当 前状态的 proccessMessage()方法处理。
有不对的地方请指正