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、案例原始碼下載
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);
}
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的原始碼講解完成,
感興趣的同學,還是自己讀一遍原始碼吧,希望我的這篇文章可以為你的原始碼閱讀提供一些幫助,
========== THE END ==========

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/31683.html
標籤:Android
