状态机的代码android并没有开放使用,在源码的frameworks/base/core/java/com/android/internal/util里StateMachine.java 、State.java 、IState.java,就这三个文件,看过源码后表示设计很精妙,必须记录下来。
一、状态机我的理解是是可以更根据当前的状态和外界的的信息输入,采取不同的对应和状态的切换。android的状态机使用如下;
继承StateMachine实现自己的状态机,在这里可继承State并自定义状态机的各状态,这里我自定义了S0,S1,S2,S3,S4,这样几个状态,在状态中重写processMessage处理当前状态下消息响应机制,我在这里只是重写了S3状态的,跳转到S2,在构造函数中使用addState函数,建立状态树,使用setInitialState初始化状态机的初使状态,start()使状态机运行。
具体使用不再写啦,源码分析以下省的忘记。
package com.example.qinxue.statemachinetest;
import android.os.Message;
import android.util.Log;
import com.example.qinxue.statemachinetest.util.State;
import com.example.qinxue.statemachinetest.util.StateMachine;
/**
* Created by qinxue on 2017/9/5.
*/
public class MyStateMachine2 extends StateMachine {
private static final String NAME = "qinxue_machine";
private static final String TAG = "StateMachine_2";
protected MyStateMachine2() {
super(NAME);
addState(s0, null);
addState(s1, s0);
addState(s2, s0);
addState(s3, s1);
addState(s4, s1);
setInitialState(s3);
start();
}
private S0 s0 = new S0();
private S1 s1 = new S1();
private S2 s2 = new S2();
private S3 s3 = new S3();
private S4 s4 = new S4();
class S0 extends State {
@Override
public void enter() {
Log.i(TAG, "S0 enter()");
super.enter();
}
@Override
public void exit() {
Log.i(TAG, "S0 exit()");
super.exit();
}
@Override
public boolean processMessage(Message msg) {
Log.i(TAG, "S0 processMessage()");
return super.processMessage(msg);
}
}
class S1 extends State {
@Override
public void enter() {
Log.i(TAG, "S1 enter()");
super.enter();
}
@Override
public void exit() {
Log.i(TAG, "S1 exit()");
super.exit();
}
@Override
public boolean processMessage(Message msg) {
Log.i(TAG, "S1 processMessage()");
return super.processMessage(msg);
}
}
class S2 extends State {
@Override
public void enter() {
Log.i(TAG, "S2 enter()");
super.enter();
}
@Override
public void exit() {
Log.i(TAG, "S2 exit()");
super.exit();
}
@Override
public boolean processMessage(Message msg) {
Log.i(TAG, "S2 processMessage()");
return super.processMessage(msg);
}
}
class S3 extends State {
@Override
public void enter() {
Log.i(TAG, "S3 enter()");
super.enter();
}
@Override
public void exit() {
Log.i(TAG, "S3 exit()");
super.exit();
}
@Override
public boolean processMessage(Message msg) {
Log.i(TAG, "S3 processMessage()");
transitionTo(s2);
return true;
}
}
class S4 extends State {
@Override
public void enter() {
Log.i(TAG, "S4 enter()");
super.enter();
}
@Override
public void exit() {
Log.i(TAG, "S4 exit()");
super.exit();
}
@Override
public boolean processMessage(Message msg) {
Log.i(TAG, "S4 processMessage()");
return super.processMessage(msg);
}
}
}
二、源码分析
1、首先在构造中使用的函数 super(NAME);这一句分析以下源码干了啥?
protected StateMachine(String name) {
// 创建HandlerThread
mSmThread = new HandlerThread(name);
mSmThread.start();
Looper looper = mSmThread.getLooper();
//
initStateMachine(name, looper);
}
创建了一个HandlerThread,其实就是有Looper的普通Thread,拿到了它的looper,貌似要初始化Handler什么的看一下initStateMachine这个方法。
private void initStateMachine(String name, Looper looper) {
mName = name;
//建立消息处理Handler
mSmHandler = new SmHandler(looper, this);
}
这里初始化了SmHandler,一个自定义Handler,没错状态机信息就是用它发送的。
2、通过addState来创建状态树,类似数据结构树,树上全是状态机的状态,改变状态时就是在这颗树的树枝上爬过去的。查看一下代码
private final StateInfo addState(State state, State parent) {
if (mDbg) {
Log.d(TAG, "addStateInternal: E state=" + state.getName()
+ ",parent=" + ((parent == null) ? "" : parent.getName()));
}
StateInfo parentStateInfo = null;
if (parent != null) {
parentStateInfo = mStateInfo.get(parent);
if (parentStateInfo == null) {
// Recursively add our parent as it's not been added yet.
parentStateInfo = addState(parent, null);
}
}
StateInfo stateInfo = mStateInfo.get(state);
if (stateInfo == null) {
stateInfo = new StateInfo();
mStateInfo.put(state, stateInfo);
}
// Validate that we aren't adding the same state in two different hierarchies.
if ((stateInfo.parentStateInfo != null) &&
(stateInfo.parentStateInfo != parentStateInfo)) {
throw new RuntimeException("state already added");
}
stateInfo.state = state;
stateInfo.parentStateInfo = parentStateInfo;
stateInfo.active = false;
if (mDbg) Log.d(TAG, "addStateInternal: X stateInfo: " + stateInfo);
return stateInfo;
}
StateInfo:保存了当前state节点,当前节点的父节点,和活动状态active这标记使在切换状态时用来爬树的,减少遍历。
mStateInfo:是hashMap保存状态节点和StateInfo,通过StateInfo可以找到父节点。
每调用addState()就往相应节点添加,根据我的代码建立如图树
setInitialState(s3);这一句代码,设定状态机的初始状态为s3,具体代码不贴了,start()之后,用红色表示节点标记为active为true,如下图。
之后我发送信息,切换状态到S2,这是S2向上便利,找到第一个红的节点(以activie来判断),这里是S0,用蓝色表示,之后按照下图的路径,红色执行exit(),蓝色执行enter(),最终切换到目标状态。s3.exit()->s1.exit()->s2.enter();