工作中有一同僚說到Android狀态機 StateMachine StateMachine StateMachine Android SDK frameworks
。作為一名Android資深工程師,我居然沒有聽說過
,是以抓緊時間學習一下。
不是
中的相關API,其存在于
層源碼中的一個Java類。可能因為如此,許多應用層的開發人員并未使用過。
是以這裡我們先說一下
StateMachine
的使用方式,然後再對源碼進行相關介紹。 - StateMachine使用舉例
- StateMachine原理學習
一、使用舉例
StateMachine 處于Android
frameworks
層源碼
frameworks/base/core/java/com/android/internal/util
路徑下。應用層若要使用
StateMachine
需将對應路徑下的三個類拷貝到自己的工程目錄下。
這三個類分别為:
StateMachine.java
、
State
IState
下邊是使用的代碼舉例,這個例子我也是網絡上找的(讀懂StateMachine源碼後,我對這個例子進行了一些簡單更改,以下為更改後的案例):
主要分以下幾個部分來說明:
- PersonStateMachine.java案例代碼
- PersonStateMachine 使用
- 案例的簡單說明
- 案例源碼下載下傳
1.1 PersonStateMachine
建立
PersonStateMachine
繼承
StateMachine
類。
建立四種狀态,四種狀态均繼承自
State
:
- 預設狀态 BoringState
- 工作狀态 WorkState
- 吃飯狀态 EatState
- 睡覺狀态 SleepState
定義了狀态轉換的四種消息類型:
- 喚醒消息 MSG_WAKEUP
- 困乏消息 MSG_TIRED
- 餓了消息 MSG_HUNGRY
- 狀态機停止消息 MSG_HALTING
下面來看完整的案例代碼:
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;
}
}
}
1.2 PersonStateMachine 使用
// 擷取 狀态機引用
PersonStateMachine personStateMachine = PersonStateMachine.makePerson();
// 初始狀态為SleepState,發送消息MSG_WAKEUP
personStateMachine.sendMessage(PersonStateMachine.MSG_WAKEUP);
-
狀态收到SleepState
消息後,會執行對應狀态的MSG_WAKEUP
方法processMessage
-
類中SleepState
方法收到processMessage
消息後,執行MSG_WAKEUP
方法,完成狀态轉換。轉換到transitionTo(mWorkState)
狀态。WorkState
1.3 案例簡單說明
幾種狀态的依賴關系如下:
構造方法中,添加所有狀态,并設定初始狀态:
PersonStateMachine(String name) {
super(name);
//加入狀态,初始化狀态
addState(mBoringState, null);
addState(mSleepState, mBoringState);
addState(mWorkState, mBoringState);
addState(mEatState, mBoringState);
// sleep狀态為初始狀态
setInitialState(mSleepState);
}
通過以下方法,建立并啟動狀态機:
public static PersonStateMachine makePerson() {
PersonStateMachine person = new PersonStateMachine("Person");
person.start();
return person;
}
1.4 案例源碼下載下傳
Android_StateMachine案例位址二、實作原理
在
StateMachine
中,開啟了一個線程
HandlerThread
,其對應的Handler為
SmHandler
。是以上文案例中對應狀态的
processMessage(Message msg)
方法,均在
HandlerThread
線程中執行。
2.1 從 StateMachine
的構造方法說起:
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
線程對應的HandlerHandlerThread
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
方法,傳回值為true,将消息消費掉,但不處理消息。進而使狀态機狀态停頓到processMessage(Message msg)
狀态mHaltingState
-
狀态,若進入該狀态, 狀态機将退出。mQuittingState
線程對應的Looper将退出,HandlerThread
線程會被銷毀,所有加入到狀态機的狀态被清空。HandlerThread
2.2 狀态機的start() 方法
狀态機的初始化說完,下邊來說狀态機的啟動方法
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堆棧中父父父狀态
- 然後moveTempStateStackToStateStack()方法中,
出棧,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()
-
在start()時,其中的内容全部不執行,是以先不介紹。performTransitions(msgProcessedState, msg);
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
堆棧狀态,也如上圖所示。
2.3 狀态轉化
還是拿案例中的代碼舉例:
// 擷取 狀态機引用
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;
}
- 在SleepState狀态的
方法中,其收到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)
方法中内容的執行示意圖如下:
A 目标狀态放入到mTempStateStack隊列中
// 目标狀态 放入temp 堆棧
// 目标狀态的 父狀态 作為參數 傳入下一級
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
- 1、将
狀态放入到WorkState
mTempStateStack
- 2、将
狀态的WorkState
一一入非活躍父狀态
堆棧mTempStateStack
- 3、因為
狀态的父狀态為WorkState
,是活躍狀态,是以隻将BoringState
放入到WorkState
mTempStateStack
- 4、傳回活躍的父狀态
BoringState
以上代碼的執行示意圖如下:
B commonStateInfo
狀态在 mStateStack
堆棧中的子狀态退堆棧
commonStateInfo
mStateStack
commonStateInfo
setupTempStateStackWithStatesToEnter(destState);
方法的傳回參數。這裡是
BoringState
// commonStateInfo 狀态的子狀态全部退棧
invokeExitMethods(commonStateInfo);
- 1、
作為參數傳入到BoringState
invokeExitMethods(commonStateInfo);
- 2、其方法内容為,将
BoringState
全部子狀态退堆棧
C mTempStateStack
全部狀态出堆棧, mStateStack
入堆棧
mTempStateStack
mStateStack
// 目标狀态入棧
int stateStackEnteringIndex = moveTempStateStackToStateStack();
// 入棧狀态 活躍
invokeEnterMethods(stateStackEnteringIndex);
-
方法中:moveTempStateStackToStateStack
mTempStateStack
mStateStack
- invokeEnterMethods(stateStackEnteringIndex);方法中,将新加入的狀态設定為
;并調用其對應的活躍狀态
enter()
最終的堆棧狀态為:
到此StateMachine的源碼講解完成。
感興趣的同學,還是自己讀一遍源碼吧,希望我的這篇文章可以為你的源碼閱讀提供一些幫助。
= THE END =
文章首發于公衆号”CODING技術小館“,如果文章對您有幫助,可關注我的公衆号。