工做中有一同事說到Android狀態機StateMachine
。做爲一名Android資深工程師,我竟然沒有據說過StateMachine
,所以抓緊時間學習一下。 StateMachine
不是Android SDK
中的相關API,其存在於frameworks
層源碼中的一個Java類。可能由於如此,許多應用層的開發人員並未使用過。 所以這裏咱們先說一下StateMachine
的使用方式,而後再對源碼進行相關介紹。java
StateMachine 處於Android frameworks
層源碼frameworks/base/core/java/com/android/internal/util
路徑下。應用層若要使用StateMachine
需將對應路徑下的三個類拷貝到本身的工程目錄下。 這三個類分別爲:StateMachine.java
、State
、IState
android
下邊是使用的代碼舉例,這個例子我也是網絡上找的(讀懂StateMachine源碼後,我對這個例子進行了一些簡單更改,如下爲更改後的案例):git
主要分如下幾個部分來講明:github
建立PersonStateMachine
繼承StateMachine
類。 建立四種狀態,四種狀態均繼承自State
:數組
定義了狀態轉換的四種消息類型:網絡
下面來看完整的案例代碼:ide
public class PersonStateMachine extends StateMachine {
private static final String TAG = "MachineTest";
//設置狀態改變標誌常量
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(); // 睡
/** * 構造方法 * * @param name */
PersonStateMachine(String name) {
super(name);
//加入狀態,初始化狀態
addState(mBoringState, null);
addState(mSleepState, mBoringState);
addState(mWorkState, mBoringState);
addState(mEatState, mBoringState);
// sleep狀態爲初始狀態
setInitialState(mSleepState);
}
/** * @return 建立啓動person 狀態機 */
public static PersonStateMachine makePerson() {
PersonStateMachine person = new PersonStateMachine("Person");
person.start();
return person;
}
@Override
protected 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;
}
}
}
複製代碼
// 獲取 狀態機引用
PersonStateMachine personStateMachine = PersonStateMachine.makePerson();
// 初始狀態爲SleepState,發送消息MSG_WAKEUP
personStateMachine.sendMessage(PersonStateMachine.MSG_WAKEUP);
複製代碼
SleepState
狀態收到MSG_WAKEUP
消息後,會執行對應狀態的processMessage
方法SleepState
類中processMessage
方法收到MSG_WAKEUP
消息後,執行transitionTo(mWorkState)
方法,完成狀態轉換。轉換到WorkState
狀態。幾種狀態的依賴關係以下: oop
構造方法中,添加全部狀態,並設置初始狀態:學習
PersonStateMachine(String name) {
super(name);
//加入狀態,初始化狀態
addState(mBoringState, null);
addState(mSleepState, mBoringState);
addState(mWorkState, mBoringState);
addState(mEatState, mBoringState);
// sleep狀態爲初始狀態
setInitialState(mSleepState);
}
複製代碼
經過如下方法,建立並啓動狀態機:ui
public static PersonStateMachine makePerson() {
PersonStateMachine person = new PersonStateMachine("Person");
person.start();
return person;
}
複製代碼
在 StateMachine
中,開啓了一個線程HandlerThread
,其對應的Handler爲SmHandler
。所以上文案例中對應狀態的 processMessage(Message msg)
方法,均在HandlerThread
線程中執行。
StateMachine
的構造方法提及,對應的代碼以下:protected StateMachine(String name) {
// 建立 HandlerThread
mSmThread = new HandlerThread(name);
mSmThread.start();
// 獲取HandlerThread對應的Looper
Looper looper = mSmThread.getLooper();
// 初始化 StateMachine
initStateMachine(name, looper);
}
複製代碼
StateMachine
的構造方法中,建立並啓動了一個線程HandlerThread
;initStateMachine
方法中,建立了HandlerThread
線程對應的Handler SmHandler
private void initStateMachine(String name, Looper looper) {
mName = name;
mSmHandler = new SmHandler(looper, this);
}
複製代碼
SmHandler
構造方法中,向狀態機中添加了兩個狀態:一個狀態爲狀態機的暫停狀態mHaltingState
、一個狀態爲狀態機的退出狀態mQuittingState
private SmHandler(Looper looper, StateMachine sm) {
super(looper);
mSm = sm;
// 添加狀態:暫停 和 退出
// 這兩個狀態 無父狀態
addState(mHaltingState, null);
addState(mQuittingState, null);
}
複製代碼
mHaltingState
狀態,顧名思義讓狀態機暫停,其對應的processMessage(Message msg)
方法,返回值爲true,將消息消費掉,但不處理消息。從而使狀態機狀態停頓到mHaltingState
狀態mQuittingState
狀態,若進入該狀態, 狀態機將退出。HandlerThread
線程對應的Looper將退出,HandlerThread
線程會被銷燬,全部加入到狀態機的狀態被清空。狀態機的初始化說完,下邊來講狀態機的啓動方法start()
public void start() {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
// StateMachine 未進行初始化,爲何不拋出一個異常
if (smh == null) {
return;
}
// 完成狀態機建設
smh.completeConstruction();
}
複製代碼
completeConstruction()
,用於完成狀態機的建設。private final void completeConstruction() {
int maxDepth = 0;
// 循環判斷全部狀態,看看哪個鏈最長,得出深度
for (StateInfo si : mStateInfoHashMap.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();
// 發送初始化完成的消息(消息放入到隊列的最前邊)
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
}
複製代碼
maxDepth
是狀態機中,最長依賴鏈的長度。mStateStack
與mTempStateStack
爲兩個用數組實現的堆棧。這兩個堆棧的最大長度,即爲maxDepth
。其用來存儲當前活躍狀態
與當前活躍狀態的父狀態、父父狀態、...等
setupInitialStateStack();
完成狀態的初始化,將當前的活躍狀態放入到mStateStack
堆棧中。下邊來具體說setupInitialStateStack();
方法中,如何完成棧的初始化。
private final void setupInitialStateStack() {
// 獲取初始狀態信息
StateInfo curStateInfo = mStateInfoHashMap.get(mInitialState);
//
for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
// 初始狀態 放入臨時堆棧
mTempStateStack[mTempStateStackCount] = curStateInfo;
// 當前狀態的 全部父狀態 一級級放入堆棧
curStateInfo = curStateInfo.parentStateInfo;
}
// 清空 狀態堆棧
// Empty the StateStack
mStateStackTopIndex = -1;
// 臨時堆棧 換到 狀態堆棧
moveTempStateStackToStateStack();
}
複製代碼
初始化狀態
放入 mTempStateStack
堆棧中初始化狀態
的父狀態
、父父狀態
、父父父狀態
... 都一一放入到mTempStateStack堆棧中mTempStateStack
出棧,mStateStack
入棧,將全部狀態信息導入到mStateStack
堆棧,並清空mTempStateStack
堆棧。到這裏,初始化基本完成,但咱們還落下一部分代碼沒有說:
// 發送初始化完成的消息(消息放入到隊列的最前邊)
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
複製代碼
SmHandler
當中。下邊來看一下SmHandler
的handleMessage(Message msg)
方法:
public final void handleMessage(Message msg) {
// 處理消息
if (!mHasQuit) {
// 保存傳入的消息
mMsg = msg;
State msgProcessedState = null;
// 已完成初始化
if (mIsConstructionCompleted) {
// ..
}
// 接收到 初始化完成的消息
else if (!mIsConstructionCompleted
&& (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
/** Initial one time path. */
// 初始化完成
mIsConstructionCompleted = true;
// 調用堆棧中狀態的enter方法,並將堆棧中的狀態設置爲活躍狀態
invokeEnterMethods(0);
} else {
// ..
}
// 執行Transition
performTransitions(msgProcessedState, msg);
}
}
複製代碼
mIsConstructionCompleted = true;
對應的標誌位變過來invokeEnterMethods
方法將mStateStack
堆棧中的全部狀態設置爲活躍狀態,並由父—>子
的順序,執行堆棧中狀態的enter()
方法performTransitions(msgProcessedState, msg);
在start()時,其中的內容所有不執行,所以先不介紹。invokeEnterMethods
方法的方法體以下:
private final void invokeEnterMethods(int stateStackEnteringIndex) {
for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
}
複製代碼
mStateStack
堆棧中的全部狀態設置爲活躍狀態,並由父—>子
的順序,執行堆棧中狀態的enter()
方法到此start()完成,最終mStateStack
堆棧狀態,也如上圖所示。
仍是拿案例中的代碼舉例:
// 獲取 狀態機引用
PersonStateMachine personStateMachine = PersonStateMachine.makePerson();
// 初始狀態爲SleepState,發送消息MSG_WAKEUP
personStateMachine.sendMessage(PersonStateMachine.MSG_WAKEUP);
複製代碼
sendMessage(PersonStateMachine.MSG_WAKEUP);
方法,向SmHandler
中發送一個消息,來觸發狀態轉化。sendMessage(PersonStateMachine.MSG_WAKEUP);
消息,爲狀態轉化的導火索。下邊,再次看一下SmHandler
的handleMessage(Message msg)
方法:
public final void handleMessage(Message msg) {
// 處理消息
if (!mHasQuit) {
// 保存傳入的消息
mMsg = msg;
State msgProcessedState = null;
// 已完成初始化
if (mIsConstructionCompleted) {
// 處理消息的狀態
msgProcessedState = processMsg(msg);
}
// 接收到 初始化完成的消息
else if (!mIsConstructionCompleted
&& (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
// 初始化完成
mIsConstructionCompleted = true;
// 調用堆棧中狀態的enter方法,並將堆棧中的狀態設置爲活躍狀態
invokeEnterMethods(0);
} else {
throw new RuntimeException("StateMachine.handleMessage: "
+ "The start method not called, received msg: " + msg);
}
// 執行Transition
performTransitions(msgProcessedState, msg);
}
}
複製代碼
processMsg(msg);
方法中。咱們來看processMsg(msg);
方法:
private final State processMsg(Message msg) {
// 堆棧中找到當前狀態
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
// 是否爲退出消息
if (isQuit(msg)) {
// 轉化爲退出狀態
transitionTo(mQuittingState);
} else {
// 狀態返回true 則是可處理此狀態
// 狀態返回false 則不能夠處理
while (!curStateInfo.state.processMessage(msg)) {
// 當前狀態的父狀態
curStateInfo = curStateInfo.parentStateInfo;
// 父狀態未null
if (curStateInfo == null) {
// 回調到未處理消息方法中
mSm.unhandledMessage(msg);
break;
}
}
}
// 消息處理後,返回當前狀態信息
// 若是消息不處理,則返回其父狀態處理,返回處理消息的父狀態
return (curStateInfo != null) ? curStateInfo.state : null;
}
複製代碼
while (!curStateInfo.state.processMessage(msg))
執行mStateStack
堆棧中,最上層狀態的 processMessage(msg)
方法。案例中這個狀態爲SleepState
mStateStack
堆棧中狀態的processMessage(msg)方法返回true,則表示其消費掉了這個消息; 若是其返回false,則表示不消費此消息,那麼該消息將繼續向其父狀態
進行傳遞;這裏,堆棧對上層的狀態爲SleepState
。因此咱們看一下其對應的processMessage(msg)
方法。
public boolean processMessage(Message msg) {
switch (msg.what) {
// 收到清醒信號
case MSG_WAKEUP:
// 進入工做狀態
transitionTo(mWorkState);
//...
//...
//發送餓了信號...
sendMessage(obtainMessage(MSG_HUNGRY));
break;
case MSG_HALTING:
// ...
break;
default:
return false;
}
return true;
}
複製代碼
processMessage(Message msg)
方法中,其收到MSG_WAKEUP
消息後,會調用transitionTo(mWorkState);
方法,將目標狀態設置爲mWorkState
。咱們看一下transitionTo(mWorkState);
方法:
private final void transitionTo(IState destState) {
mDestState = (State) destState;
}
複製代碼
transitionTo(IState destState)
方法,只是一個簡單的狀態賦值。下邊咱們回到SmHandler
的handleMessage(Message msg)
方法:
SmHandler.handleMessage(Message msg)
的performTransitions(msgProcessedState, msg);
方法之中。msgProcessedState
爲mSleepState
。private void performTransitions(State msgProcessedState, Message msg) {
// 當前狀態
State orgState = mStateStack[mStateStackTopIndex].state;
// ...
// 目標狀態
State destState = mDestState;
if (destState != null) {
while (true) {
// 目標狀態 放入temp 堆棧
// 目標狀態的 父狀態 做爲參數 傳入下一級
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
// commonStateInfo 狀態的子狀態所有退棧
invokeExitMethods(commonStateInfo);
// 目標狀態入棧
int stateStackEnteringIndex = moveTempStateStackToStateStack();
// 入棧狀態 活躍
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;
}
// ...
}
複製代碼
msgProcessedState
爲mSleepState
destState
目標狀態爲 mWorkState
此時此刻performTransitions(State msgProcessedState, Message msg)
方法中內容的執行示意圖以下:
// 目標狀態 放入temp 堆棧
// 目標狀態的 父狀態 做爲參數 傳入下一級
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
複製代碼
WorkState
狀態放入到mTempStateStack
堆棧中WorkState
狀態的非活躍父狀態
一一入mTempStateStack
堆棧WorkState
狀態的父狀態爲BoringState
,是活躍狀態,所以只將WorkState
放入到mTempStateStack
堆棧中BoringState
以上代碼的執行示意圖以下:
commonStateInfo
狀態在mStateStack
堆棧中的子狀態退堆棧commonStateInfo
爲setupTempStateStackWithStatesToEnter(destState);
方法的返回參數。這裏是BoringState
// commonStateInfo 狀態的子狀態所有退棧
invokeExitMethods(commonStateInfo);
複製代碼
BoringState
做爲參數傳入到invokeExitMethods(commonStateInfo);
方法中BoringState
狀態的所有子狀態退堆棧
以上代碼的執行示意圖以下:
mTempStateStack
所有狀態出堆棧,mStateStack
入堆棧// 目標狀態入棧
int stateStackEnteringIndex = moveTempStateStackToStateStack();
// 入棧狀態 活躍
invokeEnterMethods(stateStackEnteringIndex);
複製代碼
moveTempStateStackToStateStack
方法中:mTempStateStack
所有狀態出堆棧,mStateStack
入堆棧活躍狀態
;並調用其對應的enter()
方法。最終的堆棧狀態爲:
到此StateMachine的源碼講解完成。 感興趣的同窗,仍是本身讀一遍源碼吧,但願個人這篇文章能夠爲你的源碼閱讀提供一些幫助。