源碼分析之狀態機原型

源碼分析之狀態機原型

許久沒更新設計模式部份內容了,以前介紹了設計模式中的狀態設計模式,期間一直忙於工做上的事,對安卓源碼進行了相關的學習。翻回來看時候,發現更新到這裏的時候,就順便對源碼部分的狀態機StateMachine研究一下,其內部的設計思路就是狀態者模式的最佳體現,後續會這個基礎上詳細分說wifiStateMachine的工做方式,是狀態機的具體實現方式,基本上到這裏,狀態模式基本就講清楚了。java

  • 重要類:StateMachineandroid

  • 代碼位置:frameworks/base/core/java/com/android/internal/util/StateMachine.java設計模式

  • 狀態機,顧名思義,就是維持狀態的機器。面對平常生活,咱們常常面臨不少種狀態狀況,好比財務就是負責財務收支,工資覈對發放;保安就負責平常安保等等。每一個職業對應本身的工做職責和狀態。一個工做內容到了財務和保安都不同,各自對應本身的職責和狀態。雖然說這樣比喻不大恰當,但確實是這麼回事,這即是狀態的初始原型。狀態機內部維持一個狀態樹,狀態樹保存了每一個狀態下各自實例。每一個狀態都繼承同一個基類Status,實現接口IState,有四個方法enter(),exit(),processMessage(),getName()。狀態機中每個子類都要實現這幾個方法,對應到各自實例就會調用本身對應的方法,這即是狀態模式的做用:將請求的處理封裝到狀態類中,在不一樣狀態下對同一個請求進行不一樣的處理。數組

  • 狀態樹:狀態類的層級結構。狀態機中的每一個狀態不能隨便轉換。ide

    A		B
      	C		D	
      E		F
      	
      上面展現的狀態樹中,A狀態能夠直接轉成B狀態,相鄰狀態能夠直接轉換(注意是相鄰,直接轉換)。
      若是從狀態E準換成B,那麼要經歷一下狀態:E -> C, C -> A, A -> B;
      爲何要這麼作呢?不妨能夠這樣理解,坐電梯時候,你在門外能夠進入電梯和不進去;一旦進入電梯而且電梯運行中,你就不能隨意進出了,完成出電梯須要有一個過程。你須要讓電梯到達指定樓層,電梯中止運動,電梯開門後,你才能出電梯。
      將這種邏輯抽象出來遍生成了狀態樹,狀態樹中每一個狀態都有兩個相鄰狀態,先前狀態,後繼狀態,固然這不是必需元素。
    複製代碼
  • 狀態機範例函數

    class HelloWorld extends StateMachine {
          HelloWorld(String name) {
              super(name);
              addState(mState1);
              setInitialState(mState1);
          }
      
          public static HelloWorld makeHelloWorld() {
              HelloWorld hw = new HelloWorld("hw");
              hw.start();
              return hw;
          }
      
          class State1 extends State {
              @Override public boolean processMessage(Message message) {
                  log("Hello World");
                  return HANDLED;
              }
          }
          State1 mState1 = new State1();
      }
      
      void testHelloWorld() {
          HelloWorld hw = makeHelloWorld();
          hw.sendMessage(hw.obtainMessage());
      }
      
      1. State1繼承基類State,實現processMessage處理對應本身的邏輯
      2. HelloWorld繼承基類StateMachine狀態機,構造函數中加入狀態組成狀態樹,設置初始狀態。
      3. makeHelloWorld中爲狀態機的實例過程,其實例化自身對象,對切調用狀態機的start開啓狀態機。
      4. 測試代碼中實例化狀態機,狀態機發送消息而且處理。
    複製代碼
  • State原型oop

    public class State implements IState {
      
          protected State() {
          }
    
          @Override
          public void enter() {
          }
      
          @Override
          public void exit() {
          }
      
          @Override
          public boolean processMessage(Message msg) {
              return false;
          }
      
          public String getName() {
              String name = getClass().getName();
              int lastDollar = name.lastIndexOf('$');
              return name.substring(lastDollar + 1);
          }
      }
    複製代碼

State原型實現IState接口,須要開發者自建對應具體的狀態實現其具體邏輯,由狀態樹統一管理,再交予狀態機調用對應狀態下具體方法。源碼分析

StateMachine具體分析

  • 構造函數post

    protected StateMachine(String name) {
          mSmThread = new HandlerThread(name);
          mSmThread.start();
          Looper looper = mSmThread.getLooper();
          initStateMachine(name, looper);
      }
    
      protected StateMachine(String name, Looper looper) {
          initStateMachine(name, looper);
      }
    
      protected StateMachine(String name, Handler handler) {
          initStateMachine(name, handler.getLooper());
      }
    複製代碼

構造函數中能夠看出,StateMachine內部能夠本身維持HandlerThread,實現looper對象的消息處理,也能夠在構造中傳入。以後便進入初始化狀態機。學習

  • 初始化狀態機

    private void initStateMachine(String name, Looper looper) {
          mName = name;
          mSmHandler = new SmHandler(looper, this);
      }
    複製代碼

初始化狀態機中,其內部又一個重要的內部類SmHandler,其繼承Handler,父類不少方法都一度封裝在SmHandler內部中,咱們注重介紹一下SmHandler

  • 開啓狀態機

    public void start() {
          
          SmHandler smh = mSmHandler;
          if (smh == null) return;
          
          smh.completeConstruction();
      }
    複製代碼
  • 完成狀態樹信息以及初始化狀態信息棧

    private final void completeConstruction() {
              if (mDbg) mSm.log("completeConstruction: E");
    
          /**
           * Determine the maximum depth of the state hierarchy
           * so we can allocate the state stacks.
           */
          // 獲取狀態的最深層級
          int maxDepth = 0;
          for (StateInfo si : mStateInfo.values()) {
              int depth = 0;
              for (StateInfo i = si; i != null; depth++) {
                  i = i.parentStateInfo;
              }
              if (maxDepth < depth) {
                  maxDepth = depth;
              }
          }
          if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth);
    
          // 新建兩個狀態棧
          //1. 頂層到當前狀態信息數組
          mStateStack = new StateInfo[maxDepth];
          //2. 當前狀態信息到頂層狀態信息數組
          mTempStateStack = new StateInfo[maxDepth];
          setupInitialStateStack();
    
      		// 置前消息,初始化消息SM_INIT_CMD,由本身的handlerMessage處理
          sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
    
          if (mDbg) mSm.log("completeConstruction: X");
      }
    複製代碼

SmHandler詳細說明

  1. 幾個重要的內部成員 HashMap<State, StateInfo> mStateInfo 狀態樹成員,以HashMap形式存放,鍵State,值StateInfo

    State mInitialState
     初始化狀態信息
     
     State mDestState
     帶轉換的目的狀態
     
     ArrayList<Message> mDeferredMessages
     延遲消息列表
     
     HaltingState mHaltingState
     特殊狀態:狀態機中止狀態(內部自建)
        
     QuittingState mQuittingState
     特殊狀態:狀態機正在中止中狀態(狀態機內部自建)
     
     StateInfo mStateStack[];
     狀態樹中從頂層到初始狀態信息的信息棧
     
     StateInfo mTempStateStack[];
     狀態樹中初始狀態到頂層狀態信息的信息棧
    複製代碼
  2. handlerMessage方法分析

    該方法的主要劃分三部分,第一部分分配到對應的狀態,由對應的狀態進行處理;第二部分是狀態的初始化,執行初始化狀態路徑上每一個狀態的enter方法;第三部分是執行狀態轉移,即更新狀態樹。

    public final void handleMessage(Message msg) {
             
             ...
             
         if (mIsConstructionCompleted) {
         
             // 分配到對應的狀態,由對應的狀態進行處理,返回執行消息的狀態信息
             msgProcessedState = processMsg(msg);
         } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
                 && (mMsg.obj == mSmHandlerObj)) {
                 
             // start方法後,直接執行,執行狀態初始化(執行狀態樹上的enter方法)
             mIsConstructionCompleted = true;
             invokeEnterMethods(0);
         } else {
             throw new RuntimeException("StateMachine.handleMessage: "
                     + "The start method not called, received msg: " + msg);
         }
         
         // 狀態轉移,更新狀態樹
         performTransitions(msgProcessedState, msg);
     		
     		...
     		
     }
    複製代碼
  3. 狀態處理

    // 對應狀態(底層)處理當前消息
         private final State processMsg(Message msg) {
             StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
             if (mDbg) {
                 mSm.log("processMsg: " + curStateInfo.state.getName());
             }
    
         if (isQuit(msg)) {
             transitionTo(mQuittingState);
         } else {
             // 遞歸調用處理消息
             while (!curStateInfo.state.processMessage(msg)) {
                 /**
                  * Not processed
                  */
                 // 底層沒有處理,移交父級節點處理
                 curStateInfo = curStateInfo.parentStateInfo;
                 // 始終沒處理
                 if (curStateInfo == null) {
                     /**
                      * No parents left so it's not handled
                      */
                     mSm.unhandledMessage(msg);
                     break;
                 }
                 if (mDbg) {
                     mSm.log("processMsg: " + curStateInfo.state.getName());
                 }
             }
         }
         return (curStateInfo != null) ? curStateInfo.state : null;
     }
    複製代碼
  4. 遍歷狀態樹,各自調用enter方法

    private final void invokeEnterMethods(int stateStackEnteringIndex) {
         // 從頂層狀態樹遍歷到當前狀態下多有的enter方法,置位active爲true(對應enter方法以調用)
         for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
             if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
             mStateStack[i].state.enter();
             mStateStack[i].active = true;
         }
     }
    複製代碼
  5. 轉移狀態,更新狀態樹

    private void performTransitions(State msgProcessedState, Message msg) {
     
         // 原來的狀態信息(即mStateStack數組的頂層狀態信息)
         State orgState = mStateStack[mStateStackTopIndex].state;
    
         ...
    
         State destState = mDestState;
          // 目標狀態不爲空
         if (destState != null) {
             while (true) {
                 if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
    
                 // 查找目標狀態下對應的狀態樹(排除共有的狀態樹部分)
                 StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                 // 根據當前狀態樹頂層狀態,依次調用其enter,置位active
                 invokeExitMethods(commonStateInfo);
                 // 逆序整理兩個狀態棧信息
                 int stateStackEnteringIndex = moveTempStateStackToStateStack();
                 // 依次調用enter方法
                 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;
         }
    
         // 特殊狀態處理
         if (destState != null) {
             if (destState == mQuittingState) {
                 mSm.onQuitting();
                 cleanupAfterQuitting();
             } else if (destState == mHaltingState) {
               haltedProcessMessage(msg);
                  */
                 mSm.onHalting();
             }
         }
     }
    複製代碼
  6. 查找目標狀態下對應的狀態樹(排除共有的狀態樹部分)

    private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
         mTempStateStackCount = 0;
         // 獲取目標狀態
         StateInfo curStateInfo = mStateInfo.get(destState);
         do {
             /* 遍歷狀態樹,從目標狀態依次放入
              * 排除不可調用enter方法的狀態(activit=false)
              * temp數組存放的順序是有底層到頂層,調用時候應該逆序調用
              * 返回當前數組的末端數據(目標狀態樹下頂層狀態信息)
              */
    
             mTempStateStack[mTempStateStackCount++] = curStateInfo;
             curStateInfo = curStateInfo.parentStateInfo;
         } while ((curStateInfo != null) && !curStateInfo.active);
    
         if (mDbg) {
             mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
                     + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
         }
         return curStateInfo;
     }
    複製代碼
  7. 根據當前狀態樹頂層狀態,依次調用其enter,置位active

    private final void invokeExitMethods(StateInfo commonStateInfo) {
         while ((mStateStackTopIndex >= 0)
                 && (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
             // mStateStackTopIndex存放的頂層到底層的狀態樹信息,依次調用
             State curState = mStateStack[mStateStackTopIndex].state;
             if (mDbg) mSm.log("invokeExitMethods: " + curState.getName());
             curState.exit();
             mStateStack[mStateStackTopIndex].active = false;
             mStateStackTopIndex -= 1;
         }
     }
    複製代碼
  8. 逆序整理兩個狀態棧信息

    private final int moveTempStateStackToStateStack() {
         // 將mTempStateStack數組元素逆序保存到mStateStack中
         int startingIndex = mStateStackTopIndex + 1;
         int i = mTempStateStackCount - 1;
         int j = startingIndex;
         while (i >= 0) {
             if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
             mStateStack[j] = mTempStateStack[i];
             j += 1;
             i -= 1;
         }
    
         mStateStackTopIndex = j - 1;
         if (mDbg) {
             mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
                     + ",startingIndex=" + startingIndex + ",Top="
                     + mStateStack[mStateStackTopIndex].state.getName());
         }
         return startingIndex;
     }
    複製代碼
  9. 依次調用enter方法

    詳見前4部分
     
     private final void invokeEnterMethods(int stateStackEnteringIndex) {
     
     ...
     
     }
    複製代碼
  10. 更改狀態,直接設置中間變量mDestState,handlerMessage輪訓中不斷調用performTransitions實現狀態更改

    private final void transitionTo(IState destState) {
        mDestState = (State) destState;
        if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
    }
    
    消息輪訓處理不斷調用,更改狀態
    public final void handleMessage(Message msg) {
        if (!mHasQuit) {
            if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
            
    			...
            
            // 狀態轉移,更新狀態樹
            performTransitions(msgProcessedState, msg);
    
            ...
            
        }
    }
    複製代碼

  • 具體實現方式:

    1. 新建本身的狀態機繼承於系統的StateMachine狀態機,實現其相應的方法
    2. 完成本身需求中各類狀態的具體邏輯實現,其必須繼承自系統的State,具體邏輯實如今其自身的pressageMessage方法中。
    3. 依據示例代碼,首先構建狀態機,添加對應的狀態層級組成狀態樹,由狀態機維持,初始化狀態機,開啓狀態機便可
  • 總結:以上基本分析了StateMachine狀態機原型的工做方式和原理,其內部維持一個狀態樹,當達到當前狀態時,由維持當前狀態的實例對象處理對應的邏輯,這便對應了不一樣狀態下對同一個請求有不一樣的處理方式,當前狀態不處理遍向上傳遞父類調用,有點相似安卓中觸摸事件的向上傳遞機制。

相關拓展

設計模式之狀態模式

相關文章
相關標籤/搜索