許久沒更新設計模式部份內容了,以前介紹了設計模式中的狀態設計模式,期間一直忙於工做上的事,對安卓源碼進行了相關的學習。翻回來看時候,發現更新到這裏的時候,就順便對源碼部分的狀態機StateMachine研究一下,其內部的設計思路就是狀態者模式的最佳體現,後續會這個基礎上詳細分說wifiStateMachine的工做方式,是狀態機的具體實現方式,基本上到這裏,狀態模式基本就講清楚了。java
重要類:StateMachineandroid
代碼位置:frameworks/base/core/java/com/android/internal/util/StateMachine.java設計模式
狀態機,顧名思義,就是維持狀態的機器。面對平常生活,咱們常常面臨不少種狀態狀況,好比財務就是負責財務收支,工資覈對發放;保安就負責平常安保等等。每一個職業對應本身的工做職責和狀態。一個工做內容到了財務和保安都不同,各自對應本身的職責和狀態。雖然說這樣比喻不大恰當,但確實是這麼回事,這即是狀態的初始原型。狀態機內部維持一個狀態樹,狀態樹保存了每一個狀態下各自實例。每一個狀態都繼承同一個基類Status,實現接口IState,有四個方法enter(),exit(),processMessage(),getName()。狀態機中每個子類都要實現這幾個方法,對應到各自實例就會調用本身對應的方法,這即是狀態模式的做用:將請求的處理封裝到狀態類中,在不一樣狀態下對同一個請求進行不一樣的處理。數組
狀態樹:狀態類的層級結構。狀態機中的每一個狀態不能隨便轉換。ide
A B
C D
E F
上面展現的狀態樹中,A狀態能夠直接轉成B狀態,相鄰狀態能夠直接轉換(注意是相鄰,直接轉換)。
若是從狀態E準換成B,那麼要經歷一下狀態:E -> C, C -> A, A -> B;
爲何要這麼作呢?不妨能夠這樣理解,坐電梯時候,你在門外能夠進入電梯和不進去;一旦進入電梯而且電梯運行中,你就不能隨意進出了,完成出電梯須要有一個過程。你須要讓電梯到達指定樓層,電梯中止運動,電梯開門後,你才能出電梯。
將這種邏輯抽象出來遍生成了狀態樹,狀態樹中每一個狀態都有兩個相鄰狀態,先前狀態,後繼狀態,固然這不是必需元素。
複製代碼
狀態機範例函數
class HelloWorld extends StateMachine {
HelloWorld(String name) {
super(name);
addState(mState1);
setInitialState(mState1);
}
public static HelloWorld makeHelloWorld() {
HelloWorld hw = new HelloWorld("hw");
hw.start();
return hw;
}
class State1 extends State {
@Override public boolean processMessage(Message message) {
log("Hello World");
return HANDLED;
}
}
State1 mState1 = new State1();
}
void testHelloWorld() {
HelloWorld hw = makeHelloWorld();
hw.sendMessage(hw.obtainMessage());
}
1. State1繼承基類State,實現processMessage處理對應本身的邏輯
2. HelloWorld繼承基類StateMachine狀態機,構造函數中加入狀態組成狀態樹,設置初始狀態。
3. makeHelloWorld中爲狀態機的實例過程,其實例化自身對象,對切調用狀態機的start開啓狀態機。
4. 測試代碼中實例化狀態機,狀態機發送消息而且處理。
複製代碼
State原型oop
public class State implements IState {
protected State() {
}
@Override
public void enter() {
}
@Override
public void exit() {
}
@Override
public boolean processMessage(Message msg) {
return false;
}
public String getName() {
String name = getClass().getName();
int lastDollar = name.lastIndexOf('$');
return name.substring(lastDollar + 1);
}
}
複製代碼
State原型實現IState接口,須要開發者自建對應具體的狀態實現其具體邏輯,由狀態樹統一管理,再交予狀態機調用對應狀態下具體方法。源碼分析
構造函數post
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());
}
複製代碼
構造函數中能夠看出,StateMachine內部能夠本身維持HandlerThread,實現looper對象的消息處理,也能夠在構造中傳入。以後便進入初始化狀態機。學習
初始化狀態機
private void initStateMachine(String name, Looper looper) {
mName = name;
mSmHandler = new SmHandler(looper, this);
}
複製代碼
初始化狀態機中,其內部又一個重要的內部類SmHandler,其繼承Handler,父類不少方法都一度封裝在SmHandler內部中,咱們注重介紹一下SmHandler
開啓狀態機
public void start() {
SmHandler smh = mSmHandler;
if (smh == null) return;
smh.completeConstruction();
}
複製代碼
完成狀態樹信息以及初始化狀態信息棧
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);
// 新建兩個狀態棧
//1. 頂層到當前狀態信息數組
mStateStack = new StateInfo[maxDepth];
//2. 當前狀態信息到頂層狀態信息數組
mTempStateStack = new StateInfo[maxDepth];
setupInitialStateStack();
// 置前消息,初始化消息SM_INIT_CMD,由本身的handlerMessage處理
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
if (mDbg) mSm.log("completeConstruction: X");
}
複製代碼
幾個重要的內部成員 HashMap<State, StateInfo> mStateInfo 狀態樹成員,以HashMap形式存放,鍵State,值StateInfo
State mInitialState
初始化狀態信息
State mDestState
帶轉換的目的狀態
ArrayList<Message> mDeferredMessages
延遲消息列表
HaltingState mHaltingState
特殊狀態:狀態機中止狀態(內部自建)
QuittingState mQuittingState
特殊狀態:狀態機正在中止中狀態(狀態機內部自建)
StateInfo mStateStack[];
狀態樹中從頂層到初始狀態信息的信息棧
StateInfo mTempStateStack[];
狀態樹中初始狀態到頂層狀態信息的信息棧
複製代碼
handlerMessage方法分析
該方法的主要劃分三部分,第一部分分配到對應的狀態,由對應的狀態進行處理;第二部分是狀態的初始化,執行初始化狀態路徑上每一個狀態的enter方法;第三部分是執行狀態轉移,即更新狀態樹。
public final void handleMessage(Message msg) {
...
if (mIsConstructionCompleted) {
// 分配到對應的狀態,由對應的狀態進行處理,返回執行消息的狀態信息
msgProcessedState = processMsg(msg);
} else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
&& (mMsg.obj == mSmHandlerObj)) {
// start方法後,直接執行,執行狀態初始化(執行狀態樹上的enter方法)
mIsConstructionCompleted = true;
invokeEnterMethods(0);
} else {
throw new RuntimeException("StateMachine.handleMessage: "
+ "The start method not called, received msg: " + msg);
}
// 狀態轉移,更新狀態樹
performTransitions(msgProcessedState, msg);
...
}
複製代碼
狀態處理
// 對應狀態(底層)處理當前消息
private final State processMsg(Message msg) {
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
if (mDbg) {
mSm.log("processMsg: " + curStateInfo.state.getName());
}
if (isQuit(msg)) {
transitionTo(mQuittingState);
} else {
// 遞歸調用處理消息
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;
}
複製代碼
遍歷狀態樹,各自調用enter方法
private final void invokeEnterMethods(int stateStackEnteringIndex) {
// 從頂層狀態樹遍歷到當前狀態下多有的enter方法,置位active爲true(對應enter方法以調用)
for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
}
複製代碼
轉移狀態,更新狀態樹
private void performTransitions(State msgProcessedState, Message msg) {
// 原來的狀態信息(即mStateStack數組的頂層狀態信息)
State orgState = mStateStack[mStateStackTopIndex].state;
...
State destState = mDestState;
// 目標狀態不爲空
if (destState != null) {
while (true) {
if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
// 查找目標狀態下對應的狀態樹(排除共有的狀態樹部分)
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
// 根據當前狀態樹頂層狀態,依次調用其enter,置位active
invokeExitMethods(commonStateInfo);
// 逆序整理兩個狀態棧信息
int stateStackEnteringIndex = moveTempStateStackToStateStack();
// 依次調用enter方法
invokeEnterMethods(stateStackEnteringIndex);
// 將延遲的消息放入到隊列前面,直接處理
moveDeferredMessageAtFrontOfQueue();
if (destState != mDestState) {
// A new mDestState so continue looping
destState = mDestState;
} else {
// No change in mDestState so we're done
break;
}
}
mDestState = null;
}
// 特殊狀態處理
if (destState != null) {
if (destState == mQuittingState) {
mSm.onQuitting();
cleanupAfterQuitting();
} else if (destState == mHaltingState) {
haltedProcessMessage(msg);
*/
mSm.onHalting();
}
}
}
複製代碼
查找目標狀態下對應的狀態樹(排除共有的狀態樹部分)
private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
mTempStateStackCount = 0;
// 獲取目標狀態
StateInfo curStateInfo = mStateInfo.get(destState);
do {
/* 遍歷狀態樹,從目標狀態依次放入
* 排除不可調用enter方法的狀態(activit=false)
* temp數組存放的順序是有底層到頂層,調用時候應該逆序調用
* 返回當前數組的末端數據(目標狀態樹下頂層狀態信息)
*/
mTempStateStack[mTempStateStackCount++] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
} while ((curStateInfo != null) && !curStateInfo.active);
if (mDbg) {
mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
+ mTempStateStackCount + ",curStateInfo: " + curStateInfo);
}
return curStateInfo;
}
複製代碼
根據當前狀態樹頂層狀態,依次調用其enter,置位active
private final void invokeExitMethods(StateInfo commonStateInfo) {
while ((mStateStackTopIndex >= 0)
&& (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
// mStateStackTopIndex存放的頂層到底層的狀態樹信息,依次調用
State curState = mStateStack[mStateStackTopIndex].state;
if (mDbg) mSm.log("invokeExitMethods: " + curState.getName());
curState.exit();
mStateStack[mStateStackTopIndex].active = false;
mStateStackTopIndex -= 1;
}
}
複製代碼
逆序整理兩個狀態棧信息
private final int moveTempStateStackToStateStack() {
// 將mTempStateStack數組元素逆序保存到mStateStack中
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;
}
複製代碼
依次調用enter方法
詳見前4部分
private final void invokeEnterMethods(int stateStackEnteringIndex) {
...
}
複製代碼
更改狀態,直接設置中間變量mDestState,handlerMessage輪訓中不斷調用performTransitions實現狀態更改
private final void transitionTo(IState destState) {
mDestState = (State) destState;
if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
}
消息輪訓處理不斷調用,更改狀態
public final void handleMessage(Message msg) {
if (!mHasQuit) {
if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
...
// 狀態轉移,更新狀態樹
performTransitions(msgProcessedState, msg);
...
}
}
複製代碼
具體實現方式:
總結:以上基本分析了StateMachine狀態機原型的工做方式和原理,其內部維持一個狀態樹,當達到當前狀態時,由維持當前狀態的實例對象處理對應的邏輯,這便對應了不一樣狀態下對同一個請求有不一樣的處理方式,當前狀態不處理遍向上傳遞父類調用,有點相似安卓中觸摸事件的向上傳遞機制。