Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android狀態機statemachine詳解

android狀態機statemachine詳解

編輯:關於Android編程

 
    先說兩句題外話,很感謝android,在這裡能看到很多優秀的代碼。同時也感覺到外面的工程師真的很厲害,都是java人家就能寫出這麼牛的東西。感慨之下就有了些思考:我們絕大多數人只要把那些牛人已經創造出來的牛逼的東西,記住並且弄懂就是一件非常不錯的事情,至少能衣食無憂。:-D 讀書的時候需要經常做題,在理解的基礎上記住解題方法基本就能很牛了,事實上高考中考絕大多數都是已經有過的題型,能做到前面所說的應該能進入不錯的學校。工作後,慢慢也發現很多了不起的技術,都是在國外已經發展的很成熟基礎上學習過來的。作為一個普通人,還是不要天天談創新的好,hold不住,把基礎的東西記住掌握即可。說了一堆,也算聊以自慰。
        我們知道類的成員可以分為兩種:方法和屬性。大多數情況下,對於一個狀態,比如某數大於0,類的方法都只能做出一種對應的操作,並且類的本身並不考慮外部狀態。android的狀態機就屬於大多數之後的那一小部分。對於某個事件,或者更准確的說,某一個消息,在不同的狀態下能做出不同的操作。並且android狀態機中的狀態是繼承的,就像數據結構中的樹一樣,如果當前節點(狀態)不能對這個事件做出響應,就會到父節點繼續判斷並且做出響應,在下面的講述中,我們稱這個為狀態路徑,而對於所有狀態稱為狀態樹。這一句話已經從整體上對狀態機進行了概括,記住這些對後面的理解很有好處。
        State,狀態機中的狀態封裝類,這個類主要是實現了IState接口。其中有狀態的基本方法,enter,exit以及消息處理方法processMessage。enter方法在狀態機轉入這個狀態中會進行調用,exit方法在狀態機轉出這個方法時候會調用。這裡對於一個很簡單的類,google使用了接口屬性,說說自己的理解。接口中的方法都是公有方法,並且只能聲明常量。將主要方法都放在接口中聲明,一方面限制了方法的定義,一方面也突出了這個類主要就是擁有某種功能。另外在State裡面,聲明了一個protect類型的構造方法,這樣其他類就不可以直接聲明state類的對象。state在狀態機statemachine類裡面是以StateInfo類型使用的,這個基本不影響訪問。
statemachine裡面主要工作都是由SmHandler類來完成的,statemachine本身絕大多數方法都是都是對SmHandler方法的再次封裝。另外為了能夠做到及時響應主線程的消息,又聲明了一個HandlerThread,主要任務都是在這個線程裡面實現的。
        現在直接去看SmHandler類吧,其最主要的方法就是handleMessage。該方法的主要是三大模塊,第一個消息處理,或者說是分配到對應的狀態再有對應的狀態進行處理比較合適,第二個狀態的初始化,大概可以理解成執行初始化狀態路徑上每個狀態的enter方法,第三個執行狀態轉移,即更新狀態樹。

[java]

if (mIsConstructionCompleted) { 
    /** Normal path */ 
    processMsg(msg);//第一個消息處理  
} else if (!mIsConstructionCompleted && 
        (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) { 
    /** Initial one time path. */ 
    mIsConstructionCompleted = true; 
    invokeEnterMethods(0);//第二個狀態的初始化  
} else { 
    throw new RuntimeException("StateMachine.handleMessage: " + 
                "The start method not called, received msg: " + msg); 

performTransitions();//第三個執行狀態轉移 

            if (mIsConstructionCompleted) {
                /** Normal path */
                processMsg(msg);//第一個消息處理
            } else if (!mIsConstructionCompleted &&
                    (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
                /** Initial one time path. */
                mIsConstructionCompleted = true;
                invokeEnterMethods(0);//第二個狀態的初始化
            } else {
                throw new RuntimeException("StateMachine.handleMessage: " +
                            "The start method not called, received msg: " + msg);
            }
            performTransitions();//第三個執行狀態轉移首先去看下processMsg方法

[java]

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) { 
        Log.d(TAG, "processMsg: " + curStateInfo.state.getName()); 
    } 

                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) {
                        Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
                    }
                }從這段代碼中(!curStateInfo.state.processMessage(msg))就說明了:如果當前狀態執行完processMessage方法返回了false,也就是對當前消息NOT_HANDLED,那麼就會持續調用這個狀態的父狀態執行方法。一般終有一個狀態能夠處理消息的,如果真的沒有處理,會記錄到unhandledMessage方法裡面的。
        接下來先看下狀態轉移performTransitions方法,首先碰到mDestState,這是SmHandler裡面的一個標記,指向當前狀態路徑最下面的一個狀態,後面都是父狀態。可以在其他時候調用private final void transitionTo(IState destState)方法(外面的接口方法)改變當前mDestState的指向。這樣處理完當前消息之後,performTransitions就會根據最新的mDestState來進行狀態路徑切換。狀態切換有點像樹的遍歷一樣,並不是回到根節點在進行搜索到新的節點,而是會回到原來的mDestState和最新的mDestState最近的一個共同祖先,然後在走向新的mDestState狀態。進行狀態切換主要是執行原狀態路徑上需要退出的狀態的exit()方法,和新的狀態路徑上的enter()方法。
        最後看下狀態機的初始化invokeEnterMethods,這個直接看狀態機的實例比較方便。看一個簡單一些的應用,藍牙APK裡面關於耳機和電話連接處理的HeadsetStateMachine類,其構造方法中,關於狀態機的代碼如下:

[java]

addState(mDisconnected); 
addState(mPending); 
addState(mConnected); 
addState(mAudioOn); 
 
setInitialState(mDisconnected); 

        addState(mDisconnected);
        addState(mPending);
        addState(mConnected);
        addState(mAudioOn);

        setInitialState(mDisconnected);以上兩塊代碼,第一塊是建立本狀態機的整個框架,就相當於建立整個狀態樹,第二個是設置初始化狀態。再看看HeadsetStateMachine中的靜態代碼塊

[java]

static HeadsetStateMachine make(HeadsetService context) { 
    Log.d(TAG, "make"); 
    HeadsetStateMachine hssm = new HeadsetStateMachine(context); 
    hssm.start(); 
    return hssm; 

    static HeadsetStateMachine make(HeadsetService context) {
        Log.d(TAG, "make");
        HeadsetStateMachine hssm = new HeadsetStateMachine(context);
        hssm.start();
        return hssm;
    }以上兩塊代碼,第一塊是建立本狀態機的整個框架,就相當於建立整個狀態樹,第二個是設置初始化狀態。再看看HeadsetStateMachine中的靜態代碼塊
這裡面有一個start()方法,從這個方法開始,狀態機開始運作,包括分配內存,根據初始化狀態設置初始化狀態路徑,這一點主要在setupInitialStateStack方法中執行,依次執行狀態路徑上每個狀態的enter方法,這個使用了消息機制sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));,最終就在本段開頭的invokeEnterMethods方法中執行。
        到這裡狀態機主要內容基本講解完畢,貌似絕大多數都需要記憶,記住了感覺就理解到了。:-D 有點像本文開頭說的。初一看感覺沒有什麼,但是如果想象下你有一個這樣的需求,耳機和手機的狀態一直在切換,你會采用什麼方式去做,在考慮了很多之後會感覺狀態機真的是一個很厲害的東西。:-D 接下來附上android源碼中的demo,為了方便理解,筆者將輸出增加了一些空行,多余的空行不是demo打印的。

[java]

class Hsm1 extends StateMachine { 
    private static final String TAG = "hsm1"; 
 
    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.d(TAG, "makeHsm1 E"); 
        Hsm1 sm = new Hsm1("hsm1"); 
        sm.start(); 
        Log.d(TAG, "makeHsm1 X"); 
        return sm; 
    } 
 
    Hsm1(String name) { 
        super(name); 
        Log.d(TAG, "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.d(TAG, "ctor X"); 
    } 
 
    class P1 extends State { 
        @Override public void enter() { 
            Log.d(TAG, "mP1.enter"); 
        } 
        @Override public boolean processMessage(Message message) { 
            boolean retVal; 
            Log.d(TAG, "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.d(TAG, "mP1.exit"); 
        } 
    } 
 
    class S1 extends State { 
        @Override public void enter() { 
            Log.d(TAG, "mS1.enter"); 
        } 
        @Override public boolean processMessage(Message message) { 
            Log.d(TAG, "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.d(TAG, "mS1.exit"); 
        } 
    } 
 
    class S2 extends State { 
        @Override public void enter() { 
            Log.d(TAG, "mS2.enter"); 
        } 
        @Override public boolean processMessage(Message message) { 
            boolean retVal; 
            Log.d(TAG, "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.d(TAG, "mS2.exit"); 
        } 
    } 
 
    class P2 extends State { 
        @Override public void enter() { 
            Log.d(TAG, "mP2.enter"); 
            sendMessage(obtainMessage(CMD_5)); 
        } 
        @Override public boolean processMessage(Message message) { 
            Log.d(TAG, "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.d(TAG, "mP2.exit"); 
        } 
    } 
 
    @Override 
    void onHalting() { 
        Log.d(TAG, "halting"); 
        synchronized (this) { 
            this.notifyAll(); 
        } 
    } 
 
    P1 mP1 = new P1(); 
    S1 mS1 = new S1(); 
    S2 mS2 = new S2(); 
    P2 mP2 = new P2(); 

</code> 
 * <p>If this is executed by sending two messages CMD_1 and CMD_2 
 * (Note the synchronize is only needed because we use hsm.wait())</p> 
<code> 
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) { 
          Log.e(TAG, "exception while waiting " + e.getMessage()); 
     } 

 
 
輸出如下: 
D/hsm1    ( 1999): makeHsm1 E 
D/hsm1    ( 1999): ctor E 
D/hsm1    ( 1999): ctor X 
D/hsm1    ( 1999): mP1.enter 
D/hsm1    ( 1999): mS1.enter 
D/hsm1    ( 1999): makeHsm1 X 
 
D/hsm1    ( 1999): mS1.processMessage what=1 
 
D/hsm1    ( 1999): mS1.exit 
D/hsm1    ( 1999): mS1.enter 
 
D/hsm1    ( 1999): mS1.processMessage what=2 
D/hsm1    ( 1999): mP1.processMessage what=2 
 
D/hsm1    ( 1999): mS1.exit 
D/hsm1    ( 1999): mS2.enter 
 
D/hsm1    ( 1999): mS2.processMessage what=2 
D/hsm1    ( 1999): mS2.processMessage what=3 
 
D/hsm1    ( 1999): mS2.exit 
D/hsm1    ( 1999): mP1.exit 
D/hsm1    ( 1999): mP2.enter 
 
D/hsm1    ( 1999): mP2.processMessage what=3 
D/hsm1    ( 1999): mP2.processMessage what=4 
D/hsm1    ( 1999): mP2.processMessage what=5 
D/hsm1    ( 1999): mP2.exit 
D/hsm1    ( 1999): halting 

class Hsm1 extends StateMachine {
    private static final String TAG = "hsm1";

    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.d(TAG, "makeHsm1 E");
        Hsm1 sm = new Hsm1("hsm1");
        sm.start();
        Log.d(TAG, "makeHsm1 X");
        return sm;
    }

    Hsm1(String name) {
        super(name);
        Log.d(TAG, "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.d(TAG, "ctor X");
    }

    class P1 extends State {
        @Override public void enter() {
            Log.d(TAG, "mP1.enter");
        }
        @Override public boolean processMessage(Message message) {
            boolean retVal;
            Log.d(TAG, "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.d(TAG, "mP1.exit");
        }
    }

    class S1 extends State {
        @Override public void enter() {
            Log.d(TAG, "mS1.enter");
        }
        @Override public boolean processMessage(Message message) {
            Log.d(TAG, "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.d(TAG, "mS1.exit");
        }
    }

    class S2 extends State {
        @Override public void enter() {
            Log.d(TAG, "mS2.enter");
        }
        @Override public boolean processMessage(Message message) {
            boolean retVal;
            Log.d(TAG, "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.d(TAG, "mS2.exit");
        }
    }

    class P2 extends State {
        @Override public void enter() {
            Log.d(TAG, "mP2.enter");
            sendMessage(obtainMessage(CMD_5));
        }
        @Override public boolean processMessage(Message message) {
            Log.d(TAG, "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.d(TAG, "mP2.exit");
        }
    }

    @Override
    void onHalting() {
        Log.d(TAG, "halting");
        synchronized (this) {
            this.notifyAll();
        }
    }

    P1 mP1 = new P1();
    S1 mS1 = new S1();
    S2 mS2 = new S2();
    P2 mP2 = new P2();
}
</code>
 * <p>If this is executed by sending two messages CMD_1 and CMD_2
 * (Note the synchronize is only needed because we use hsm.wait())</p>
<code>
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) {
          Log.e(TAG, "exception while waiting " + e.getMessage());
     }
}


輸出如下:
D/hsm1    ( 1999): makeHsm1 E
D/hsm1    ( 1999): ctor E
D/hsm1    ( 1999): ctor X
D/hsm1    ( 1999): mP1.enter
D/hsm1    ( 1999): mS1.enter
D/hsm1    ( 1999): makeHsm1 X

D/hsm1    ( 1999): mS1.processMessage what=1

D/hsm1    ( 1999): mS1.exit
D/hsm1    ( 1999): mS1.enter

D/hsm1    ( 1999): mS1.processMessage what=2
D/hsm1    ( 1999): mP1.processMessage what=2

D/hsm1    ( 1999): mS1.exit
D/hsm1    ( 1999): mS2.enter

D/hsm1    ( 1999): mS2.processMessage what=2
D/hsm1    ( 1999): mS2.processMessage what=3

D/hsm1    ( 1999): mS2.exit
D/hsm1    ( 1999): mP1.exit
D/hsm1    ( 1999): mP2.enter

D/hsm1    ( 1999): mP2.processMessage what=3
D/hsm1    ( 1999): mP2.processMessage what=4
D/hsm1    ( 1999): mP2.processMessage what=5
D/hsm1    ( 1999): mP2.exit
D/hsm1    ( 1999): halting

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved