Android StateMachine學(xué)習(xí)

在看藍(lán)牙源碼的時候暖眼,發(fā)現(xiàn)藍(lán)牙的連接狀態(tài)以及綁定狀態(tài)是通過StateMachine(狀態(tài)機(jī))來實(shí)現(xiàn)的。通過StateMachine來管理不同狀態(tài)下的行為動作纺裁,提高靈活性诫肠。除了藍(lán)牙,wifi、wps同樣是通過StateMachine來處理狀態(tài)栋豫。

先舉兩個個例子來看看StateMachine是如何使用的惭缰,例子是從源碼注釋里頭直接copy來的。在StateMachine源碼中可以看到它被@Hide了笼才,所以開發(fā)中漱受,我們是用不了。

  • 例1
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 void enter() {
            super.enter();
            log("State1 enter");
        }

        @Override
        public void exit() {
            super.exit();
            log("State1 exit");
        }

        Override 
        public boolean processMessage(Message message) {
            log("Hello World");
            return HANDLED;
        }
    }
    State1 mState1 = new State1();
}

// 測試代碼
void testHelloWorld() {
    HelloWorld hw = makeHelloWorld();
    hw.sendMessage(hw.obtainMessage());
}

執(zhí)行結(jié)果:

State1 enter
Hello World

從這個例子中可以了解到StateMachine的使用流程分三步走:

1. `add(state)` 添加狀態(tài)骡送,將所有的狀態(tài)都add進(jìn)去;
2. `setInitialState(state)` 設(shè)置初始狀態(tài)
3. `start()` 啟動狀態(tài)機(jī)

從日志上可以看出昂羡,狀態(tài)機(jī)啟動之后,初始狀態(tài)的enter()方法會被執(zhí)行摔踱,然后往狀態(tài)機(jī)里頭發(fā)送message虐先,初始狀態(tài)的processMessage(msg)方法會被執(zhí)行。由于沒有切換到其他狀態(tài)派敷,所以exit()方法未被執(zhí)行蛹批。

  • 例2
class Hsm1 extends StateMachine {
    public static final int CMD_1 = 1;
    public static final int CMD_2 = 2;
    public static final int CMD_3 = 3;
    public static final int CMD_4 = 4;
    public static final int CMD_5 = 5;

    public static Hsm1 makeHsm1() {
        log("makeHsm1 E");
        Hsm1 sm = new Hsm1("hsm1");
        sm.start();
        log("makeHsm1 X");
        return sm;
    }

    Hsm1(String name) {
        super(name);
        log("ctor E");

        // Add states, use indentation to show hierarchy
        addState(mP1);
        addState(mS1, mP1);
        addState(mS2, mP1);
        addState(mP2);

        // Set the initial state
        setInitialState(mS1);
        log("ctor X");
    }

    class P1 extends State {
        Override 
        public void enter() {
            log("mP1.enter");
        }
        Override 
        public boolean processMessage(Message message) {
            boolean retVal;
            log("mP1.processMessage what=" + message.what);
            switch(message.what) {
            case CMD_2:
                // CMD_2 will arrive in mS2 before CMD_3
                sendMessage(obtainMessage(CMD_3));
                deferMessage(message);
                transitionTo(mS2);
                retVal = HANDLED;
                break;
            default:
                // Any message we don't understand in this state invokes unhandledMessage
                retVal = NOT_HANDLED;
                break;
            }
            return retVal;
        }
        Override 
        public void exit() {
            log("mP1.exit");
        }
    }

    class S1 extends State {
        Override 
        public void enter() {
            log("mS1.enter");
        }
        Override 
        public boolean processMessage(Message message) {
            log("S1.processMessage what=" + message.what);
            if (message.what == CMD_1) {
                // Transition to ourself to show that enter/exit is called
                transitionTo(mS1);
                return HANDLED;
            } else {
                // Let parent process all other messages
                return NOT_HANDLED;
            }
        }
        Override 
        public void exit() {
            log("mS1.exit");
        }
    }

    class S2 extends State {
        Override 
        public void enter() {
            log("mS2.enter");
        }

        Override 
        public boolean processMessage(Message message) {
            boolean retVal;
            log("mS2.processMessage what=" + message.what);
            switch(message.what) {
            case(CMD_2):
                sendMessage(obtainMessage(CMD_4));
                retVal = HANDLED;
                break;
            case(CMD_3):
                deferMessage(message);
                transitionTo(mP2);
                retVal = HANDLED;
                break;
            default:
                retVal = NOT_HANDLED;
                break;
            }
            return retVal;
        }

        Override 
        public void exit() {
            log("mS2.exit");
        }
    }

    class P2 extends State {
        Override 
        public void enter() {
            log("mP2.enter");
            sendMessage(obtainMessage(CMD_5));
        }

        Override 
        public boolean processMessage(Message message) {
            log("P2.processMessage what=" + message.what);
            switch(message.what) {
            case(CMD_3):
                break;
            case(CMD_4):
                break;
            case(CMD_5):
                transitionToHaltingState();
                break;
            }
            return HANDLED;
        }

        Override 
        public void exit() {
            log("mP2.exit");
        }
    }

    Override
    void onHalting() {
        log("halting");
        synchronized (this) {
            this.notifyAll();
        }
    }

    P1 mP1 = new P1();
    S1 mS1 = new S1();
    S2 mS2 = new S2();
    P2 mP2 = new P2();
}


// 測試代碼
Hsm1 hsm = makeHsm1();
synchronize(hsm) {
     hsm.sendMessage(obtainMessage(hsm.CMD_1));
     hsm.sendMessage(obtainMessage(hsm.CMD_2));
     try {
          // wait for the messages to be handled
          hsm.wait();
     } catch (InterruptedException e) {
          loge("exception while waiting " + e.getMessage());
     }
}

這個例子中有4個狀態(tài),5條命令篮愉,多次的狀態(tài)切換腐芍。

  • step1: 構(gòu)建狀態(tài)機(jī)并添加狀態(tài),添加完?duì)顟B(tài)之后的狀態(tài)樹如下:

            mP1           mP2
           /   \
    初始值->mS1 mS2
    

    其中mP1是mS1和mS2的父狀態(tài)。

  • step2: 調(diào)用start()方法试躏,啟動狀態(tài)機(jī)猪勇,此時會打印如下日志:

      makeHsm1 E
      ctor E
      ctor X
      mP1.enter
      mS1.enter
      makeHsm1 X
    

    雖然設(shè)置的初始狀態(tài)是mS1,但是mP1的enter()方法也被調(diào)用颠蕴,具體原因會在原理分析中說明泣刹。

  • step3: 發(fā)送CMD_1指令,當(dāng)前狀態(tài)是S1犀被,并且S1會處理該指令事件椅您,通過方法transitionTo(mS1)將狀態(tài)重新切換到S1,此時會打印的日志如下:

    mS1.processMessage what=1
    mS1.exit        
    mS1.enter
    

    調(diào)用transitionTo()方法的時候寡键,上一個狀態(tài)會先調(diào)用exit()方法掀泳,然后新的狀態(tài)調(diào)用enter()方法:oldState.exit() -> newState.enter()

  • step4: 發(fā)送CMD_2指令昌腰,此時的日志如下:

     mS1.processMessage what=2
     mP1.processMessage what=2
    

    S1中是不處理CMD_2的开伏,但是它的父狀態(tài)會處理,那么就交給P1去處理遭商,具體原因后面分析固灵。

  • step5:P1接收到CMD_2指令之后,發(fā)送CMD_3指令劫流,并且通過deferMessage(CMD_2)方法將CMD_2放到消息隊(duì)列的頂端巫玻,然后切換到S2狀態(tài)丛忆,日志如下:

    mS1.exit
    mS2.enter
    mS2.processMessage what=2
    mS2.processMessage what=3
    

    上個狀態(tài)是S1,切換到S2仍秤,所以S1exit(), S2enter(),由于S1和S2是父狀態(tài)都是P1熄诡,那么P1維持不變。

  • step6:S2依次接收到CMD_2诗力、CMD_3指令凰浮,先后執(zhí)行發(fā)送CMD_4指令、defermessage(CMD_3)苇本、切換到P2狀態(tài)袜茧,日志如下:

     mS2.exit
     mP1.exit
     mP2.enter
     mP2.processMessage what=3
     mP2.processMessage what=4
    

    S2切換到P2,S2先exit()瓣窄,然后它父狀態(tài)P1exit()笛厦,最后P2執(zhí)行enter(),接著陸續(xù)處理CMD_3俺夕、CMD_4事件裳凸。

  • step6:P2在執(zhí)行enter()方法的時候,發(fā)送了CMD_5指令劝贸,CMD_5指令是停止?fàn)顟B(tài)機(jī)姨谷,那么就將執(zhí)行P2的exit()。日志如下:

    mP2.exit
    halting
    

StateMachine使用總結(jié)

  1. 在狀態(tài)機(jī)的構(gòu)造方法里頭悬荣,通過addState()方法構(gòu)建狀態(tài)樹菠秒;
  2. 通過setInitialState()設(shè)置初始狀態(tài)疙剑;
  3. 通過start()方法啟動狀態(tài)機(jī)氯迂,初始狀態(tài)執(zhí)行enter()方法;
  4. sendMessage(msg)給狀態(tài)機(jī)發(fā)送消息之后言缤,當(dāng)前狀態(tài)會執(zhí)行processMessage(msg)嚼蚀,來處理消息,如果當(dāng)前狀態(tài)處理不了管挟,則交給父狀態(tài)處理轿曙,如果都處理不了,交給狀態(tài)機(jī)處理僻孝;
  5. 通過transitionTo(state)來切換狀態(tài)导帝,oldState執(zhí)行exit(),newState執(zhí)行enter()穿铆;
  6. deferMessage(msg)暫時將當(dāng)前消息保存到消息隊(duì)列的頂端您单, 一旦切換到新的狀態(tài),首先處理該消息荞雏;
  7. 切換到某個狀態(tài)的時候虐秦,先會從當(dāng)前狀態(tài)開始往根狀態(tài)依次執(zhí)行各狀態(tài)的exit()方法平酿,直到與新狀態(tài)相同的父狀態(tài)Px為止。然后依次執(zhí)行從Px到新狀態(tài)路徑下各狀態(tài)的enter()方法(需要注意的是悦陋,公共父狀態(tài)的enter和exit方法不會執(zhí)行)蜈彼。

參考1
參考2

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市俺驶,隨后出現(xiàn)的幾起案子幸逆,更是在濱河造成了極大的恐慌,老刑警劉巖暮现,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秉颗,死亡現(xiàn)場離奇詭異,居然都是意外死亡送矩,警方通過查閱死者的電腦和手機(jī)蚕甥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來栋荸,“玉大人菇怀,你說我怎么就攤上這事∩慰椋” “怎么了爱沟?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長匆背。 經(jīng)常有香客問我呼伸,道長,這世上最難降的妖魔是什么钝尸? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任括享,我火速辦了婚禮,結(jié)果婚禮上珍促,老公的妹妹穿的比我還像新娘铃辖。我一直安慰自己,他們只是感情好猪叙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布娇斩。 她就那樣靜靜地躺著,像睡著了一般穴翩。 火紅的嫁衣襯著肌膚如雪犬第。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天芒帕,我揣著相機(jī)與錄音歉嗓,去河邊找鬼。 笑死副签,一個胖子當(dāng)著我的面吹牛遥椿,可吹牛的內(nèi)容都是我干的基矮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼冠场,長吁一口氣:“原來是場噩夢啊……” “哼家浇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起碴裙,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤钢悲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后舔株,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莺琳,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年载慈,在試婚紗的時候發(fā)現(xiàn)自己被綠了惭等。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡办铡,死狀恐怖辞做,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寡具,我是刑警寧澤秤茅,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站童叠,受9級特大地震影響框喳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厦坛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一五垮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧粪般,春花似錦拼余、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凡橱。三九已至小作,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稼钩,已是汗流浹背顾稀。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坝撑,地道東北人静秆。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓粮揉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親抚笔。 傳聞我的和親對象是個殘疾皇子扶认,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • StateMachine 的簡單使用 步驟 源碼的frameworks/base/core/java/com/an...
    JustinBetter閱讀 7,305評論 3 7
  • 前言 HI,歡迎來到《每周一博》殊橙。今天是十一月第五周辐宾,我給大家介紹一下安卓系統(tǒng)中的狀態(tài)機(jī)。為什么會介紹狀態(tài)機(jī)呢膨蛮?因...
    健身營養(yǎng)愛好者閱讀 9,573評論 0 13
  • 前言 最近在了解WIFI模塊時叠纹,發(fā)現(xiàn)WifiController類繼承至StateMachine,而WifiCon...
    GrayMonkey閱讀 4,062評論 0 4
  • 概念 有限狀態(tài)機(jī)即FSM敞葛,簡稱狀態(tài)機(jī)誉察,表示有限個狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動作等行為的數(shù)學(xué)模型。 狀態(tài)機(jī)可以描...
    Galileo_404閱讀 10,693評論 0 8
  • 又來到了一個老生常談的問題惹谐,應(yīng)用層軟件開發(fā)的程序員要不要了解和深入學(xué)習(xí)操作系統(tǒng)呢冒窍? 今天就這個問題開始,來談?wù)劜?..
    tangsl閱讀 4,124評論 0 23