工作中有一同事說到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源碼後,我對這個例子進行了一些簡單更改,以下為更改後的案例):
主要分以下幾個部分來說明:
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;
}
}
}
// 獲取 狀態機引用
PersonStateMachine personStateMachine = PersonStateMachine.makePerson();
// 初始狀態為SleepState,發送消息MSG_WAKEUP
personStateMachine.sendMessage(PersonStateMachine.MSG_WAKEUP);
幾種狀態的依賴關係如下:
構造方法中,添加所有狀態,並設置初始狀態:
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;
}
Android_StateMachine案例地址
https://github.com/AndroidHighQualityCodeStudy/Android_-StateMachine
二、實現原理學習在 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);
}
private void initStateMachine(String name, Looper looper) {
mName = name;
mSmHandler = new SmHandler(looper, this);
}
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();
}
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));
}
下邊來具體說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();
}
到這裡,初始化基本完成,但我們還落下一部分代碼沒有說:
// 發送初始化完成的消息(消息放入到隊列的最前邊)
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
下邊來看一下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;
}
}
到此start()完成,最終mStateStack堆棧狀態,也如上圖所示。
2.3、狀態轉化還是拿案例中的代碼舉例:
// 獲取 狀態機引用
PersonStateMachine personStateMachine = PersonStateMachine.makePerson();
// 初始狀態為SleepState,發送消息MSG_WAKEUP
personStateMachine.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);方法:
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;
}
我們看一下transitionTo(mWorkState);方法:
private final void transitionTo(IState destState) {
mDestState = (State) 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;
}
// ...
}
此時此刻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
以上代碼的執行示意圖如下:
commonStateInfo為setupTempStateStackWithStatesToEnter(destState);方法的返回參數。這裡是BoringState
// commonStateInfo 狀態的子狀態全部退棧
invokeExitMethods(commonStateInfo);
以上代碼的執行示意圖如下:
// 目標狀態入棧
int stateStackEnteringIndex = moveTempStateStackToStateStack();
// 入棧狀態 活躍
invokeEnterMethods(stateStackEnteringIndex);
最終的堆棧狀態為:
到此StateMachine的源碼講解完成。
感興趣的同學,還是自己讀一遍源碼吧,希望我的這篇文章可以為你的源碼閱讀提供一些幫助。