在上一篇中,我简单介绍了一下StateMachine的工作原理,并尝试用示意图的方式描述其运行机制。
这一篇会从源码入手,对StateMachine进行一个详细的拆解;
注意,如下所有代码片段均非源代码直接截取,而是经过了各种删减的,删减部分包括但不限于日志输出部分、不影响代码逻辑分析的分支判断等;同时,为了方便阅读、理解,对原有的英文注释也进行了删减,并添加了部分中文的,基于个人的理解;因此请勿直接使用;
类与内部类
State
首先需要介绍一下State这个类;
前面提到,StateMachine是处理状态与消息两类事物的工具类,消息通过Message表征,那么状态就需要借助State类来表达了:
这个由State.java提供支持,其继承自IState,并完成了如下几个抽象方法的空实现:
//当状态机切换到该状态时调用
public void enter();
//当状态机切离该状态时调用
public void exit();
/*
* 当状态机切换到该状态后调用,
* 返回true表示已经处理完所有事件;
* 返回false表示该状态没有处理任何信息;
* 状态机会根据其返回值决定下一步的处理逻辑,这里先不赘述,下面
* 会讲到
*/
@Override
public boolean processMessage(Message msg) {
return false;
}
并完成了toString()方法的重写:
@Override
public String getName() {
String name = getClass().getName();
int lastDollar = name.lastIndexOf('$');
return name.substring(lastDollar + 1);
}
显然,要使用时需要继承State并重写对应方法以满足需求;
StateMachine
StateMachine本质上是的“半成品”,具体使用时需要继承并重写部分空实现,此处仅分析StateMachine.java的逻辑;
StateMachine的构造方法有一下几个:
protected StateMachine(String name) {
mSmThread = new HandlerThread(name);
mSmThread.start();
Looper looper = mSmThread.getLooper();
initStateMachine(name, looper);
}
protected StateMachine(String name, Looper looper) {
initStateMachine(name, looper);
}
protected StateMachine(String name, Handler handler) {
initStateMachine(name, handler.getLooper());
}
默认有3个构造方法,参数1始终为字符串name,参数2可以为Handler/Looper,或者没有参数2;
- 参数1会直接传递给initStateMachine方法赋值给mName;
- 参数2会转化为Looper对象传递给initStateMachine方法用于构造mSmHandler;
- 如果没有参数2,则通过创建一个HandlerThread,并将其对应Looper获取到,再传递给initStateMachine方法用于构造mSmHandler;
在构造方法中,均会调用到initStateMachine方法:
private SmHandler mSmHandler;
private HandlerThread mSmThread;
private void initStateMachine(String name, Looper looper) {
mName = name;
mSmHandler = new SmHandler(looper, this);
}
initStateMachine中会构造出StateMachine中大部分逻辑实现的SmHandler对象;
SmHandler
继承自Handler,也是StateMachine主要依赖的机制,其构造方法如下:
//内部维护的一个表征“正在挂起”状态的State子类对象
private HaltingState mHaltingState = new HaltingState();
//内部维护的一个表征“正在退出”状态的State子类对象
private QuittingState mQuittingState = new QuittingState();
//内部类实例对象对外部StateMachine类实例的引用,用于进行各种回调
private StateMachine mSm;
/*
* 构造方法,参数1为Looper,用于确定该Handler运行的线程;
* 参数2为StateMachine的实例对象,用于存到成员变量中,
* 便于后续进行各种回调;
*/
private SmHandler(Looper looper, StateMachine sm) {
super(looper);
mSm = sm;
//添加两个必然需要的状态:正在挂起、正在退出
addState(mHaltingState, null);
addState(mQuittingState, null);
}
/*
* addState实现,参数1表示需要添加的状态,参数2指定该状态
* 关联的父类状态,如果该状态独立存在,则参数2为null
*/
private final StateInfo addState(State state, State parent) {
StateInfo parentStateInfo = null;
if (parent != null) {
parentStateInfo = mStateInfo.get(parent);
if (parentStateInfo == null) {
//如果parent也没有通过addState添加过,则先递归调用,添加parent
parentStateInfo = addState(parent, null);
}
}
StateInfo stateInfo = mStateInfo.get(state);
if (stateInfo == null) {
//该状态没有添加过,则构造StateInfo对象将其封装保存;
stateInfo = new StateInfo();
//添加到HashMap<State, StateInfo> mStateInfo中
mStateInfo.put(state, stateInfo);
}
if ((stateInfo.parentStateInfo != null)
&& (stateInfo.parentStateInfo != parentStateInfo)) {
//如果待添加的State已经存在于其他parent下,则抛出异常
throw new RuntimeException("state already added");
}
stateInfo.state = state;
stateInfo.parentStateInfo = parentStateInfo;
stateInfo.active = false;
//返回封装好的StateInfo对象
return stateInfo;
}
在StateMachine调用start方法后,会调用SmHandler内部的completeConstruction方法进行状态初始化:
private final void completeConstruction() {
int maxDepth = 0;
//遍历所有已经通过addState添加的状态,并获取所有层级关系中最复杂的一个对应的深度
for (StateInfo si : mStateInfo.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();
/** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
}
private final void setupInitialStateStack() {
//mInitialState通过StateMachine.setInitialState()设置
StateInfo curStateInfo = mStateInfo.get(mInitialState);
//这里旨在将mInitialState按照自下往上的先后顺序,添加到mTempStateStack中
for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
mTempStateStack[mTempStateStackCount] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
}
mStateStackTopIndex = -1;
//最后再将整个mTempStateStack的内容反序添加到mStateStack中
moveTempStateStackToStateStack();
}
private final int moveTempStateStackToStateStack() {
int startingIndex = mStateStackTopIndex + 1;
int i = mTempStateStackCount - 1;
int j = startingIndex;
while (i >= 0) {
if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
mStateStack[j] = mTempStateStack[i];
j += 1;
i -= 1;
}
mStateStackTopIndex = j - 1;
if (mDbg) {
mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
+ ",startingIndex=" + startingIndex + ",Top="
+ mStateStack[mStateStackTopIndex].state.getName());
}
return startingIndex;
}
在初始化完成后,StateMachine就运行起来了,此时只需要按照需求进行对应方法调用即可;
前面提到,StateMachine主要是处理状态切换、消息通知两个方法,前者通过transitionTo实现,后者通过sendMessage及其类似方法完成,并调用SmHandler的对应方法通知到SmHandler中;
而作为Handler的一个子类,SmHandler处理消息是在handleMessage方法中:
public final void handleMessage(Message msg) {
if (!mHasQuit) {
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
/*
* 除去StateMachine start/stop调用时发送的Message,
* 其余所有Message均会在mSm不为null时
* handleMessage初期回调StateMachine的onPreHandleMessage方法
*(默认空实现,可按需继承后重写);
*/
mSm.onPreHandleMessage(msg);
}
//保存为成员变量
mMsg = msg;
//根据msg确认对应的State
State msgProcessedState = null;
if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) {
//初始化后所有调用发送的Message都会走这个逻辑分支
msgProcessedState = 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(msgProcessedState, msg);
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
/*
* 除去StateMachine start/stop调用时发送的Message,
* 其余所有Message均会在mSm不为null时
* handleMessage初期回调StateMachine的onPostHandleMessage方法
*(默认空实现,可按需继承后重写);
*/
mSm.onPostHandleMessage(msg);
}
}
}
这里为了理解方便,暂时先不讨论不同State的层级关系(hierarchy),我计划在下一篇中介绍
通过阅读SmHandler的handleMessage方法实现,我们可以得出:
- 真正的状态切换,是在performTransitions方法中完成的,并包含了上一状态的exit回调以及新状态的enter回调;
- StateMachine的transitionTo方法本身只是将目标状态赋值给了mDestState,没有实际完成状态的切换,也就是不会立即调用enter;
- 要想触发上一状态的exit与新状态的enter回调,必须在transitionTo调用后再通过sendMessage(或类似方法)发送一条消息给到SmHandler,通过handleMessage方法来完成状态的切换与相关回调;
SmHandler.StateInfo
这是一个SmHandler的内部类,用于表征一个State的所属关系;
private class StateInfo {
//StateInfo表征的State对象
State state;
//该State的父节点
StateInfo parentStateInfo;
//是否活跃,当StateMachine切换到该状态时为true,切离时改为false
//即:调用对应的State的enter方法后改为true,exit方法后为false
boolean active;
@Override
public String toString() {
return "state=" + state.getName() + ",active=" + active + ",parent="
+ ((parentStateInfo == null) ? "null" : parentStateInfo.state.getName());
}
}
LogRec
(日志相关,非核心逻辑部分,暂不深入分析;)
用于表示单条状态切换记录;主要记录了如下信息:
//状态机的引用
private StateMachine mSm;
//构造、更新的时间戳
private long mTime;
//取自update方法时传入的Message对象的what变量,可用于表征状态
private int mWhat;
/* 取自update方法传入的String类型参数
* 默认由StateMachine的getLogRecString方法提供
* 默认返回空串;
*/
private String mInfo;
/* 取自update方法传入的第一个IState类型参数
* 默认由StateMachine的getLogRecString方法提供
* 默认返回空串;
*/
private IState mState;
/* 取自update方法传入的第二个IState类型参数
* 与调用时的 mStateStack[mStateStackTopIndex].state变量信息一致;
* 表示状态切换前的那个状态;
*/
private IState mOrgState;
/* 取自update方法传入的第三个IState类型参数
* 与调用时的mDestState变量信息一致;
*/
private IState mDstState;
这里关于mState/mOrgState/mDstState三个变量的关系,会在下一篇介绍State的层级关系(hierarchy)时展开,这里不影响对整个StateMachine工作逻辑的理解,可以先忽略;
LogRecords
(日志相关,非核心逻辑部分,暂不深入分析;)
内部维护了一个泛型为LogRec的向量(Vector),用于记录所有的LogRec记录,默认最大容量为20,可通过调用StateMachine的setLogRecSize方法修改容量;
使用方法
主要的常用方法
//用于添加新的State
public final void addState(State state, State parent);
public final void addState(State state);
//用于删除State
public final void removeState(State state);
//用于切换State
public final void transitionTo(IState destState);
//用于直接切换到“正在挂起”状态
public final void transitionToHaltingState();
//用于调用completeConstruction方法进行状态初始化,并向SmHandler中发送SM_INIT_CMD方法进行初始化;handleMessage在识别到SM_INIT_CMD消息时,会调用invokeEnterMethods方法,逐一调用所有已经添加的State的enter方法;
public void start();
//与SmHandler(Handler)的调用一致,其内部实现也是调用mSmHandler的对应重载方法
public final Message obtainMessage();(包括重载方法)
public void sendMessage(int what);
public void sendMessage(Message msg);
public void sendMessageDelayed(int what, int arg1, long delayMillis);(包括重载方法)
protected final void sendMessageAtFrontOfQueue(int what);(包括重载方法)
protected final void removeMessages(int what);(包括重载方法)
public final void deferMessage(Message msg);
protected final void removeDeferredMessages(int what);
//通知SmHandler发送SM_QUIT_CMD消息,并在handleMessage时调用到transitionTo(mQuittingState)与cleanupAfterQuitting(),后者会停止Looper,并释放所有引用,清空所有数据,至此,该状态机不再可用。后续只能通过构造新的实例对象(调用start方法也无法重新启用)
public final void quit();
//与quit()的区别仅在于sendMessage与sendMessageAtFrontOfQueue的区别;
public final void quitNow();
//通过SmHandler设置初始状态,并将其赋值给SmHandler内部的成员变量mInitialState
public final void setInitialState(State initialState);
注意
在调用StateMachine的start方法之前,至少需要完成如下调用:
public final void setInitialState(State initialState);
public final void addState(State state, State parent);//或其他重载方法
如果不调用setInitialState,mInitialState在start后依旧为null,SM_INIT_CMD走到performTransitions()时mStateStackTopIndex仍然为-1,导致数组下标越界;
此外,由于setInitialState的参数必须是已经通过addState添加的State(否则mStateInfo中不会有对应的StateInfo),setupInitialStateStack方法时也不会向mStateStack中添加有效元素,因此mStateStackTopIndex依旧为01,同样会导致数组下标越界;
结尾
这篇是从源码结构拆分的角度进行的代码分析,比较零散,但方便有各种需求的人查阅;
接下来会计划再写一篇,着重介绍下State的层级关系(hierarchy)