最近独立开发了项目的一个部分,关于设计模式方面总是经过很多改动也没有得到最理想的结构,刚好借着这个机会详细了解一下Android的状态机设计思想,选取的demo代码就是WifiStateMachine,为了能详细的剖析该设计思想,将WiFi处理逻辑相关的代码尽可能的忽略,只抓住该类的设计模式进行分析。
首先是状态的构建:
// CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
addState(mInitialState, mDefaultState);
addState(mSupplicantStartingState, mDefaultState);
addState(mSupplicantStartedState, mDefaultState);
addState(mScanModeState, mSupplicantStartedState);
addState(mConnectModeState, mSupplicantStartedState);
addState(mL2ConnectedState, mConnectModeState);
addState(mObtainingIpState, mL2ConnectedState);
addState(mConnectedState, mL2ConnectedState);
addState(mRoamingState, mL2ConnectedState);
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
addState(mWpsRunningState, mConnectModeState);
addState(mWaitForP2pDisableState, mSupplicantStartedState);
addState(mSupplicantStoppingState, mDefaultState);
addState(mSoftApState, mDefaultState);
// CHECKSTYLE:ON IndentationCheck
可以看到WiFi模块的状态比较复杂,可以用一个树的形式来表示,概念图如下:
添加状态机的函数的实现在父类StateMachine中,如下:
/**
* Add a new state to the state machine. Bottom up addition
* of states is allowed but the same state may only exist
* in one hierarchy.
*
* @param state the state to add
* @param parent the parent of state
* @return stateInfo for this state
*/
private final StateInfo addState(State state, State parent) {
if (mDbg) {
mSm.log("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) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
return stateInfo;
}
经过一些初始化如设置初始状态后会调用start来启动状态机
/**
* Start the state machine.
*/
public void start() {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
/** Send the complete construction message */
smh.completeConstruction();
}
/**
* Complete the construction of the state machine.
*/
private final void completeConstruction() {
if (mDbg) mSm.log("completeConstruction: E");
/**
* Determine the maximum depth of the state hierarchy
* so we can allocate the state stacks.
*/
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) mSm.log("completeConstruction: maxDepth=" + maxDepth);
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));
if (mDbg) mSm.log("completeConstruction: X");
}
该方法计算出了状态机层次结构的深度,同时维护了两个状态数组mStateStack 和mTempStateStack ,并将初始状态开始直至根节点的状态存储到mTempStateStack中,将该结果再逆序存储到mStateStack 中。
之后调用sendMessageAtFrontOfQueue方法,通过mSmHandler将SM_INIT_CMD指令发送到StateMachine的MessageQueue中,其中mSmHandler的构造是在StateMachine的构造中完成的:
protected StateMachine(String name) {
mSmThread = new HandlerThread(name);
mSmThread.start();
Looper looper = mSmThread.getLooper();
mName = name;
mSmHandler = new SmHandler(looper, this);
}
接下来就是状态机对指令的核心处理逻辑handleMessage:
/**
* Handle messages sent to the state machine by calling
* the current state's processMessage. It also handles
* the enter/exit calls and placing any deferred messages
* back onto the queue when transitioning to a new state.
*/
@Override
public final void handleMessage(Message msg) {
if (!mHasQuit) {
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPreHandleMessage(msg);
}
if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
/** Save the current message */
mMsg = msg;
/** State that processed the message */
State msgProcessedState = null;
if (mIsConstructionCompleted) {
/** Normal path */
// processMsg会依次调用各个状态内部的processMessage
msgProcessedState = processMsg(msg);
} else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
&& (mMsg.obj == mSmHandlerObj)) {
/** Initial one time path. */
mIsConstructionCompleted = true;
invokeEnterMethods(0);
} else {
throw new RuntimeException("StateMachine.handleMessage: "
+ "The start method not called, received msg: " + msg);
}
// 经过上一步的处理之后mDestState已经被改变,需要整理状态树
performTransitions(msgProcessedState, msg);
// We need to check if mSm == null here as we could be quitting.
if (mDbg && mSm != null) mSm.log("handleMessage: X");
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPostHandleMessage(msg);
}
}
}
processMsg的实现如下:
/**
* Process the message. If the current state doesn't handle
* it, call the states parent and so on. If it is never handled then
* call the state machines unhandledMessage method.
* @return the state that processed the message
*/
private final State processMsg(Message msg) {
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
if (mDbg) {
mSm.log("processMsg: " + curStateInfo.state.getName());
}
if (isQuit(msg)) {
// 如果msg.what == SM_QUIT_CMD直接切换到退出状态
transitionTo(mQuittingState);
} else {
/**
* 从状态树的最小子节点开始寻找能够processMessage的状态,
* 如果找不到就向上移到他的父节点看是否能处理
* 在某个状态处理了processMessage之后,会改变mDestState为下一个状态
*/
while (!curStateInfo.state.processMessage(msg)) {
/**
* Not processed
*/
curStateInfo = curStateInfo.parentStateInfo;
if (curStateInfo == null) {
/**
* No parents left so it's not handled
*/
mSm.unhandledMessage(msg);
break;
}
if (mDbg) {
mSm.log("processMsg: " + curStateInfo.state.getName());
}
}
}
// 返回处理了这个事件的状态
return (curStateInfo != null) ? curStateInfo.state : null;
}
由于第一次调用handleMessage函数的时候未进行初始化,因此会调用invokeEnterMethods来将状态进行初始化:
mStateStack[i].state.enter();
mStateStack[i].active = true;
在执行完初始化逻辑后会调用performTransitions函数,这个函数的实现比较复杂,目的是完成下一个目的状态对应的状态树的enter调用和已完成状态对应的状态树中无用状态的exit调用。由于初始化环节destState == null,所以不会走到复杂逻辑中,但是为了之后的分析,我们将这个函数梳理一遍。
/**
* Do any transitions
* @param msgProcessedState is the state that processed the message
*/
private void performTransitions(State msgProcessedState, Message msg) {
/**
* If transitionTo has been called, exit and then enter
* the appropriate states. We loop on this to allow
* enter and exit methods to use transitionTo.
*/
State orgState = mStateStack[mStateStackTopIndex].state;
/**
* Record whether message needs to be logged before we transition and
* and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which
* always set msg.obj to the handler.
*/
boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);
if (mLogRecords.logOnlyTransitions()) {
/** Record only if there is a transition */
if (mDestState != null) {
mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
orgState, mDestState);
}
} else if (recordLogMsg) {
/** Record message */
mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,
mDestState);
}
State destState = mDestState;
if (destState != null) {
/**
* Process the transitions including transitions in the enter/exit methods
*/
while (true) {
if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
/**
* Determine the states to exit and enter and return the
* common ancestor state of the enter/exit states. Then
* invoke the exit methods then the enter methods.
*/
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
invokeExitMethods(commonStateInfo);
// 将临时堆栈中的内容复制到状态树中,并且将终点记录为mStateStackTopIndex,将起点返回
int stateStackEnteringIndex = moveTempStateStackToStateStack();
invokeEnterMethods(stateStackEnteringIndex);
/**
* Since we have transitioned to a new state we need to have
* any deferred messages moved to the front of the message queue
* so they will be processed before any other messages in the
* message queue.
*/
moveDeferredMessageAtFrontOfQueue();
if (destState != mDestState) {
// A new mDestState so continue looping
destState = mDestState;
} else {
// No change in mDestState so we're done
break;
}
}
mDestState = null;
}
/**
* After processing all transitions check and
* see if the last transition was to quit or halt.
*/
if (destState != null) {
if (destState == mQuittingState) {
/**
* Call onQuitting to let subclasses cleanup.
*/
mSm.onQuitting();
cleanupAfterQuitting();
} else if (destState == mHaltingState) {
/**
* Call onHalting() if we've transitioned to the halting
* state. All subsequent messages will be processed in
* in the halting state which invokes haltedProcessMessage(msg);
*/
mSm.onHalting();
}
}
}
setupTempStateStackWithStatesToEnter的实现如下:
private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
/**
* Search up the parent list of the destination state for an active
* state. Use a do while() loop as the destState must always be entered
* even if it is active. This can happen if we are exiting/entering
* the current state.
*/
mTempStateStackCount = 0;
// curStateInfo 为要切换的目的状态
StateInfo curStateInfo = mStateInfo.get(destState);
do {
// 以目的状态为起点,循环记录目的状态树中未激活的状态,并将这些状态依次压入mTempStateStack中
mTempStateStack[mTempStateStackCount++] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
} while ((curStateInfo != null) && !curStateInfo.active);
if (mDbg) {
mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
+ mTempStateStackCount + ",curStateInfo: " + curStateInfo);
}
// 返回的状态是已经被激活的,其实是旧状态树与新状态树的交点
return curStateInfo;
}
invokeExitMethods的实现如下,目的是将旧状态树中不再使用的状态退出,调用exit方法,active 置为false,通过setupTempStateStackWithStatesToEnter函数已经找出了新老状态树的交点,也就是清理的终点:
/**
* Call the exit method for each state from the top of stack
* up to the common ancestor state.
*/
private final void invokeExitMethods(StateInfo commonStateInfo) {
while ((mStateStackTopIndex >= 0)
&& (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
State curState = mStateStack[mStateStackTopIndex].state;
if (mDbg) mSm.log("invokeExitMethods: " + curState.getName());
curState.exit();
// 将标志位置为false,等待后续回收清理
mStateStack[mStateStackTopIndex].active = false;
mStateStackTopIndex -= 1;
}
}
之后调用moveTempStateStackToStateStack()和invokeEnterMethods(stateStackEnteringIndex),代码实现相同,这里不再进行赘述,至此旧状态对指令的处理,状态的切换、旧状态的清理和新状态的enter已全部完成。
接下来选取状态机真正执行流程中的一部分来分析setSupplicantRunning函数:
if (enable) {
sendMessage(CMD_START_SUPPLICANT);
} else {
sendMessage(CMD_STOP_SUPPLICANT);
}
这个指令会由InitialState来处理:
case CMD_START_SUPPLICANT:
mClientInterface = mWifiNative.setupForClientMode();
if (mClientInterface == null
|| !mDeathRecipient.linkToDeath(mClientInterface.asBinder())) {
setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
cleanup();
break;
}
try {
// A runtime crash or shutting down AP mode can leave
// IP addresses configured, and this affects
// connectivity when supplicant starts up.
// Ensure we have no IP addresses before a supplicant start.
mNwService.clearInterfaceAddresses(mInterfaceName);
// Set privacy extensions
mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
// IPv6 is enabled only as long as access point is connected since:
// - IPv6 addresses and routes stick around after disconnection
// - kernel is unaware when connected and fails to start IPv6 negotiation
// - kernel can start autoconfiguration when 802.1x is not complete
mNwService.disableIpv6(mInterfaceName);
} catch (RemoteException re) {
loge("Unable to change interface settings: " + re);
} catch (IllegalStateException ie) {
loge("Unable to change interface settings: " + ie);
}
if (!mWifiNative.enableSupplicant()) {
loge("Failed to start supplicant!");
setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
cleanup();
break;
}
if (mVerboseLoggingEnabled) log("Supplicant start successful");
mWifiMonitor.startMonitoring(mInterfaceName, true);
setSupplicantLogLevel();
// 将mDestState设置为mSupplicantStartingState
transitionTo(mSupplicantStartingState);
break;
调用native函数进行核心的处理逻辑后会调用transitionTo将状态切换到SupplicantStartingState,具体的切换逻辑已在上文分析过,setSupplicantRunning函数通过WifiStateMachine执行CMD_START_SUPPLICANT逻辑并切换状态等待下一步的指令。
分析到这项目中的实现已经有了大概的想法,最后总结一下,Android已经给出了很多现成的轮子,但是很多时候照搬轮子会造成技术浪费,统观WifiStateMachine,具有复杂的状态切换逻辑,原因是WiFi多种的状态,而自己要实现的功能是否需要如此复杂还需要进一步研讨。
目前刚完成了一个核心就是状态机思想的项目,但相比于Android的WifiStateMachine还是小巫见大巫,主要区别一是使用的语言不同,二是我的状态机只有当前状态能处理事件,而WifiStateMachine是从整个状态树中寻找可以处理事件的状态,找到后的状态切换也不是简单的单状态更改,而是更改整个状态树。
最后附上学习状态机时写的demo代码,可以方便与大家理解:
https://github.com/woyelaishishiba/StateMachineDemo