Android frameworks源碼StateMachine使用舉例及源碼解析 工作中有一同事說到Android狀態機 。作為一名Android資深工程師,我居然沒有聽說過 ,因此抓緊時間學習一下。 不是 中的相關API,其存在於 層源碼中的一個Java類。可能因為如此,許多應用層的開發人員並未 ...
Android frameworks源碼StateMachine使用舉例及源碼解析
工作中有一同事說到Android狀態機StateMachine
。作為一名Android資深工程師,我居然沒有聽說過StateMachine
,因此抓緊時間學習一下。
StateMachine
不是Android SDK
中的相關API,其存在於frameworks
層源碼中的一個Java類。可能因為如此,許多應用層的開發人員並未使用過。
因此這裡我們先說一下StateMachine
的使用方式,然後再對源碼進行相關介紹。
- 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.java
創建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、案例源碼下載
二、實現原理學習
在 StateMachine
中,開啟了一個線程HandlerThread
,其對應的Handler為SmHandler
。因此上文案例中對應狀態的 processMessage(Message msg)
方法,均在HandlerThread
線程中執行。
2.1、首先從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
線程對應的HandlerSmHandler
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
線程會被銷毀,所有加入到狀態機的狀態被清空。
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()
方法 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
堆棧狀態,也如上圖所示。
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
為setupTempStateStackWithStatesToEnter(destState);
方法的返回參數。這裡是BoringState
// commonStateInfo 狀態的子狀態全部退棧
invokeExitMethods(commonStateInfo);
- 1、
BoringState
作為參數傳入到invokeExitMethods(commonStateInfo);
方法中 - 2、其方法內容為,將
BoringState
狀態的全部子狀態退堆棧
以上代碼的執行示意圖如下:
C、mTempStateStack
全部狀態出堆棧,mStateStack
入堆棧
// 目標狀態入棧
int stateStackEnteringIndex = moveTempStateStackToStateStack();
// 入棧狀態 活躍
invokeEnterMethods(stateStackEnteringIndex);
moveTempStateStackToStateStack
方法中:mTempStateStack
全部狀態出堆棧,mStateStack
入堆棧- invokeEnterMethods(stateStackEnteringIndex);方法中,將新加入的狀態設置為
活躍狀態
;並調用其對應的enter()
方法。
最終的堆棧狀態為:
到此StateMachine的源碼講解完成。
感興趣的同學,還是自己讀一遍源碼吧,希望我的這篇文章可以為你的源碼閱讀提供一些幫助。