概述
如果流程围绕失误的状态流转,这时候就要用到状态机,状态机描述一个事务,有多种状态,不同的动作作用再状态上导致抓状态的转换,这里面有三个重点
- 状态 : 睡觉,工作,吃饭
- 事件 : 起床,饥饿,疲惫
- 动作 : 比如说闹铃触发了
起床 事件导致状态 从睡觉->工作(可以省略)
总体就是,首先触发某个事件,导致了状态的改变, 闹铃 触发起床事件 ,导致状态的改变睡觉-->工作
而Android中提供了状态机,在frameworks 层源码frameworks/base/core/java/com/android/internal/util ,如果项目中需要使用可以把对应的三个类拷贝到项目中StateMachine.java、State、IState
源码分析
IState
public interface IState {
static final boolean HANDLED = true;
static final boolean NOT_HANDLED = false;
void enter();
void exit();
boolean processMessage(Message msg);
String getName();
}
状态的接口,定义了基本的方法,State实现了IState,我们自定义的状态需要直接继承State
StateMachine
构造方法
private void initStateMachine(String name, Looper looper) {
mName = name;
mSmHandler = new SmHandler(looper, this);
}
protected StateMachine(String name) {
mSmThread = new HandlerThread(name);
mSmThread.start();
Looper looper = mSmThread.getLooper();
initStateMachine(name, looper);
}
public StateMachine(String name, Looper looper) {
initStateMachine(name, looper);
}
private void initStateMachine(String name, Looper looper) {
mName = name;
mSmHandler = new SmHandler(looper, this);
}
有三个构造方法,可以外部传入Looper,如果外部不传入就自动 new HandlerThread ,最终创建SmHandler ,他是StateMachine的内部类,他的角色相当于上面说的动作
addState
private HashMap<State, StateInfo> mStateInfo =new HashMap<State, StateInfo>();
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) {
parentStateInfo = addState(parent, null);
}
}
StateInfo stateInfo = mStateInfo.get(state);
if (stateInfo == null) {
stateInfo = new StateInfo();
mStateInfo.put(state, stateInfo);
}
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;
}
private class StateInfo {
State state;
StateInfo parentStateInfo;
boolean active;
@Override
public String toString() {
return "state=" + state.getName() + ",active=" + active
+ ",parent=" + ((parentStateInfo == null) ?
"null" : parentStateInfo.state.getName());
}
}
像状态机添加状态,可以看到最外层使用HashMap 存储key=State,value=StateInfo ,而StateInfo中储存了当前状态和是否激活,和当前状态的父节点
假如说目前有六个状态,A->B->C 和 D->E->F,C是B的父节点,B是A的父节点
setInitialState 设置除初始化状态
public final void setInitialState(State initialState) {
mSmHandler.setInitialState(initialState);
}
private final void setInitialState(State initialState) {
if (mDbg) Log.d(TAG, "setInitialState: initialState=" + initialState.getName());
mInitialState = initialState;
}
假如现在设的初始状态为 C
状态机开始
public void start() {
if (mSmHandler == null) return;
mSmHandler.completeConstruction();
}
private final void completeConstruction() {
int maxDepth = 0;
for (StateInfo si : mStateInfo.values()) {
int depth = 0;
for (StateInfo i = si; i != null; depth++) {
i = i.parentStateInfo;
}
if (maxDepth < depth) {
maxDepth = depth;
}
}
if (mDbg) Log.d(TAG, "completeConstruction: maxDepth=" + maxDepth);
mStateStack = new StateInfo[maxDepth];
mTempStateStack = new StateInfo[maxDepth];
setupInitialStateStack();
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
if (mDbg) Log.d(TAG, "completeConstruction: X");
}
private final void setupInitialStateStack() {
if (mDbg) {
Log.d(TAG, "setupInitialStateStack: E mInitialState="
+ mInitialState.getName());
}
StateInfo curStateInfo = mStateInfo.get(mInitialState);
for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
mTempStateStack[mTempStateStackCount] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
}
mStateStackTopIndex = -1;
moveTempStateStackToStateStack();
}
private final int moveTempStateStackToStateStack() {
int startingIndex = mStateStackTopIndex + 1;
int i = mTempStateStackCount - 1;
int j = startingIndex;
while (i >= 0) {
if (mDbg) Log.d(TAG, "moveTempStackToStateStack: i=" + i + ",j=" + j);
mStateStack[j] = mTempStateStack[i];
j += 1;
i -= 1;
}
mStateStackTopIndex = j - 1;
if (mDbg) {
Log.d(TAG, "moveTempStackToStateStack: X mStateStackTop="
+ mStateStackTopIndex + ",startingIndex=" + startingIndex
+ ",Top=" + mStateStack[mStateStackTopIndex].state.getName());
}
return startingIndex;
}
这里一共做了一下几件事情
- 计算出状态树的最大深度
- 根据最大深度初始化俩个数组
- 然后根据初始的State 填充数组
此时数组状态,也就是说从mStateStack 按照mStateStackTopIndex 取出的状态是C
Handler处理初始化
public final void handleMessage(Message msg) {
if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what);
mMsg = msg;
if (mIsConstructionCompleted) {
processMsg(msg);
} else if (!mIsConstructionCompleted &&
(mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
mIsConstructionCompleted = true;
invokeEnterMethods(0);
} else {
throw new RuntimeException("StateMachine.handleMessage: " +
"The start method not called, received msg: " + msg);
}
performTransitions();
if (mDbg) Log.d(TAG, "handleMessage: X");
}
private final void invokeEnterMethods(int stateStackEnteringIndex) {
for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
if (mDbg) Log.d(TAG, "invokeEnterMethods: " + mStateStack[i].state.getName());
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
}
第一次初始化做了俩件事情
- 首先把`mIsConstructionCompleted = true;
- 然后把invokeEnterMethods(0)方法,由于传入的是0,所以把mStateStack中所有的状态都调用
mStateStack[i].state.enter();mStateStack[i].active = true; 全部激活
如果已经初始化完成了调用processMsg
private final void processMsg(Message msg) {
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
if (mDbg) {
Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
}
if (isQuit(msg)) {
transitionTo(mQuittingState);
} else {
while (!curStateInfo.state.processMessage(msg)) {
curStateInfo = curStateInfo.parentStateInfo;
if (curStateInfo == null) {
mSm.unhandledMessage(msg);
break;
}
if (mDbg) {
Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
}
}
}
这个就做了俩件事情
- 首先从
mStateStack 取出顶部状态(目前来说就是取出了C) - 调用
State的processMessage 方法,如果没处理就调用父节点,如果父节点也不处理,就提跳出循环
怎么切换状态呢?
private final void transitionTo(IState destState) {
mDestState = (State) destState;
if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());
}
用这个方法切换状态,参数就是目标状态,我们看到再handleMessage中,除了调用State 的processMessage 方法,还调用了performTransitions 来处理状态的切换,看下这个方法
private void performTransitions() {
State destState = null;
while (mDestState != null) {
if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");
destState = mDestState;
mDestState = null;
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
invokeExitMethods(commonStateInfo);
int stateStackEnteringIndex = moveTempStateStackToStateStack();
invokeEnterMethods(stateStackEnteringIndex);
moveDeferredMessageAtFrontOfQueue();
}
}
假如目标状态为F ,先走setupTempStateStackWithStatesToEnter
private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
mTempStateStackCount = 0;
StateInfo curStateInfo = mStateInfo.get(destState);
do {
mTempStateStack[mTempStateStackCount++] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
} while ((curStateInfo != null) && !curStateInfo.active);
if (mDbg) {
Log.d(TAG, "setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
+ mTempStateStackCount + ",curStateInfo: " + curStateInfo);
}
return curStateInfo;
}
这个就是按照顺序把destState和他的父节点依次填入mTempStateStack,这里返回值为null,因为新的状态都没有被激活过,此时mTemStateStack数据为
然后调用invokeExitMethods(commonStateInfo);
private final void invokeExitMethods(StateInfo commonStateInfo) {
while ((mStateStackTopIndex >= 0) &&
(mStateStack[mStateStackTopIndex] != commonStateInfo)) {
State curState = mStateStack[mStateStackTopIndex].state;
if (mDbg) Log.d(TAG, "invokeExitMethods: " + curState.getName());
curState.exit();
mStateStack[mStateStackTopIndex].active = false;
mStateStackTopIndex -= 1;
}
}
这里表示把之前mStateStack数据exit,并且active = false,此时mStateStack 状态为
接下来调用moveTempStateStackToStateStack
private final int moveTempStateStackToStateStack() {
int startingIndex = mStateStackTopIndex + 1;
int i = mTempStateStackCount - 1;
int j = startingIndex;
while (i >= 0) {
if (mDbg) Log.d(TAG, "moveTempStackToStateStack: i=" + i + ",j=" + j);
mStateStack[j] = mTempStateStack[i];
j += 1;
i -= 1;
}
mStateStackTopIndex = j - 1;
if (mDbg) {
Log.d(TAG, "moveTempStackToStateStack: X mStateStackTop="
+ mStateStackTopIndex + ",startingIndex=" + startingIndex
+ ",Top=" + mStateStack[mStateStackTopIndex].state.getName());
}
return startingIndex;
}
这个就是把mTempStateStack翻转填充mStateStack,此时mStateStack状态为,此时返回值为0
最后调用
private final void invokeEnterMethods(int stateStackEnteringIndex) {
for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
if (mDbg) Log.d(TAG, "invokeEnterMethods: " + mStateStack[i].state.getName());
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
}
把mStateStack中的状态激活,此时抓状态就装换完毕了,下次handle处理数据 StateInfo curStateInfo = mStateStack[mStateStackTopIndex] 就是拿到的新的状态
这只是讨论其中一种情况切换到了状态F,如果切换到状态B呢?有些区差异,但基本差不多
使用
public class MyStateMachine extends StateMachine {
private static final String TAG = "mmm";
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();
MyStateMachine(String name) {
super(name);
addState(mBoringState, null);
addState(mSleepState, mBoringState);
addState(mWorkState, mBoringState);
addState(mEatState, mBoringState);
setInitialState(mSleepState);
}
public static MyStateMachine makePerson() {
MyStateMachine person = new MyStateMachine("Person");
person.start();
return person;
}
@Override
public 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;
}
}
}
调用
MyStateMachine personStateMachine = MyStateMachine.makePerson();
personStateMachine.sendMessage(MyStateMachine.MSG_WAKEUP);
日志
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: ############ enter Boring ############
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: ############ enter Sleep ############
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: SleepState processMessage.....
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: SleepState MSG_WAKEUP
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: ############ exit Sleep ############
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: ############ enter Work ############
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: WorkState processMessage.....
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: WorkState MSG_HUNGRY
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: ############ exit Work ############
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: ############ enter Eat ############
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: EatState processMessage.....
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: EatState MSG_TIRED
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: ############ exit Eat ############
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: ############ enter Sleep ############
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: SleepState processMessage.....
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: SleepState MSG_HALTING
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: ############ exit Sleep ############
2021-08-12 18:20:03.137 6035-8981/com.example.myapplication E/mmm: ############ exit Boring ############
这里最重要的是要分清楚 状态和事件,首先触发某个事件,导致了状态的改变, 闹铃 触发起床事件 ,导致状态的改变睡觉-->工作
这里首先把所有的状态都加入到了状态机,然后设置初始状态是为Sleep ,然后就调用了start
所以开始就会把Sleep和其父节点 加入状态栈中,然后调用enter,然后调用personStateMachine.sendMessage(MyStateMachine.MSG_WAKEUP); 这里可以这样理解,sendMessage 表示都动作,MyStateMachine.MSG_WAKEUP 表示事件,然后SleepState接收事件情,触发状态的改变 transitionTo(mWorkState);
也就是说当前状态SleepState 只接受接收事件MSG_WAKEUP ,如果是其他事件,当前状态不接受,也就不会改变状态,比如当前状态时睡觉,触发事件吃饭,睡觉时不能吃饭,所以是个无效事件
|