Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中的設計模式-狀態模式

Android中的設計模式-狀態模式

編輯:關於Android編程

狀態模式說明

“狀態模式允許一個對象在其內部狀態改變的時候改變其行為。這個對象看上去就像是改變了它的類一樣。” –《JAVA與模式》
管理者持有多種狀態,狀態切換後,管理者調用Handle方法時,間接調用狀態類中的Handle方法,從而根據當前狀態的不同,就可以表現出不同的結果。

狀態模式類圖

這裡寫圖片描述

狀態模式使用示例

例如一個變形金剛,假設它有3中狀態:汽車狀態,人形狀態,飛機狀態。 不同的狀態進行戰斗的時候,采用的方式不同,分別是“撞擊碾壓”,“赤手空拳”和“導彈射擊”。各個類及方法見下面類圖

代碼

State接口

public interface State {
    public void fight();
}

CarState

public class CarState implements State {
    private static String  stateName ="汽車形態";

    @Override
    public void fight() {
        System.out.println("橫沖直撞碾壓");
    }


    @Override
    public String toString() {
        return stateName;
    }
}

FlightState

public class FlightState implements State {
    private static String  stateName ="飛機形態";
    @Override
    public void fight() {
        System.out.println("發射導彈攻擊");
    }
    @Override
    public String toString() {
        return stateName;
    }
}

HumanState

public class HumanState implements State {
    private static String  stateName ="人性形態";
    @Override
    public void fight() {
        System.out.println("赤手空拳搏斗");
    }
    @Override
    public String toString() {
        return stateName;
    }
}

Transformer類

public class Transformer {
    private String name;
    private State currentState;
    public State  transformTo(State state){
        this.currentState=state;
        return this.currentState;
    }
    public void fight(){
        this.currentState.fight();
    }
    public Transformer(String name,State currentState) {
        this.name=name;
        this.currentState = currentState;
    }

}

測試類

public class TransformerTest {
     public static void main(String[] args){
         State currentState;
         //創建初始形態
         State initState=new CarState();
         //創建變形金剛
         Transformer bumblebee= new Transformer("大黃蜂", initState);
         //開始戰斗
         bumblebee.fight();
         //切換到人形形態
         currentState= bumblebee.transformTo(new HumanState());
         System.out.println("切換到:"+currentState);
         bumblebee.fight();
         //切換到飛機形態
         currentState= bumblebee.transformTo(new FlightState());
         System.out.println("切換到:"+currentState);
         bumblebee.fight();
     }
}

輸出結果

橫沖直撞碾壓
切換到:人性形態
赤手空拳搏斗
切換到:飛機形態
發射導彈攻擊

Android源碼中的使用舉例

Android系統源代碼中有一個名為StateMachine的工具類,該類是一個分層狀態機,處理各種State類的轉化。State狀態類必須實現processMessage方法,為了創建/摧毀工作環境,還可以繼承實現enter/exit等方法。

相比較前面所說的基本的狀態模式,StateMachine可以在每一個狀態內,定義其接收不同的指令,會切換到哪個狀態,而不需要狀態機主動去設定狀態,降低了主體和狀態之間的耦合,增加一個新狀態時更加方便。

狀態機建立

當一個StateMachine對象建立後,可以通過addState()函數來設定狀態機有哪些狀態,通過setInitialState()來設定初始的狀態。通過start()方法來初始化並啟動虛擬機。

addState(State state, State parent) ----state為當前增加的狀態,parent為當前狀態的父狀態。

狀態機啟動時首先調用初始State的enter函數來初始化當前狀態,並且是從最頂層的父狀態開始調用,然後再向下調用到子狀態的enter。

        mP1
       /   \
      mS2   mS1 ----> initial state

如上所示,當設定mS1為初始狀態時,會依次調用mP1 mS1的enter函數來初始化環境。如下代碼所展示的,mStateStack是狀態從父到子的一個StateInfo數組。StateInfo是和一個State綁定的。包含了當前狀態,父狀態,當前是否激活。

        private final void invokeEnterMethods(int stateStackEnteringIndex) {
            for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
                if (mDbg) Log.d(TAG, "invokeEnterMethods: " + mStateStack[i].state.getName());
                mStateStack[i].state.enter();
                mStateStack[i].active = true;
            }
        }

可以看看這個數組的初始化過程:
1,首先依次把當前狀態及循環追溯父類的狀態保存在mTempStateStack臨時的棧中。

        private final void setupInitialStateStack() {
            if (mDbg) {
                Log.d(TAG, "setupInitialStateStack: E mInitialState="
                    + mInitialState.getName());
            }


            StateInfo curStateInfo = mStateInfo.get(mInitialState);
            for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
                mTempStateStack[mTempStateStackCount] = curStateInfo;
                curStateInfo = curStateInfo.parentStateInfo;
            }


            // Empty the StateStack
            mStateStackTopIndex = -1;


            moveTempStateStackToStateStack();
        }

2,然後把臨時的棧倒序,並保存在mStateStack中,這時mStateStack從0開始就是最頂端的父類,然後依次保存子類。

        private final int moveTempStateStackToStateStack() {
            int startingIndex = mStateStackTopIndex + 1;
            int i = mTempStateStackCount - 1;
            int j = startingIndex;
            while (i >= 0) {
                if (mDbg) Log.d(TAG, "moveTempStackToStateStack: i=" + i + ",j=" + j);
                mStateStack[j] = mTempStateStack[i];
                j += 1;
                i -= 1;
            }


            mStateStackTopIndex = j - 1;
            if (mDbg) {
                Log.d(TAG, "moveTempStackToStateStack: X mStateStackTop="
                      + mStateStackTopIndex + ",startingIndex=" + startingIndex
                      + ",Top=" + mStateStack[mStateStackTopIndex].state.getName());
            }
            return startingIndex;
        }

狀態機運行

狀態機啟動之後,通過調用狀態機的StateMachine.obtainMessage()函數來獲取消息,通過StateMachine.sendMessage()函數來發送消息,狀態機接收到這個消息後,就會調用當前狀態的processMessage()函數來根據當前狀態中定義好的方式,進行狀態的切換。

        public final void handleMessage(Message msg) {
            if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what);


            /** Save the current message */
            mMsg = msg;


            if (mIsConstructionCompleted) {
                /** Normal path */
                processMsg(msg);
                ......
        private final void processMsg(Message msg) {
            StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
            if (mDbg) {
                Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
            }


            if (isQuit(msg)) {
                transitionTo(mQuittingState);
            } else {
                while (!curStateInfo.state.processMessage(msg)) { //調用當前狀態的processMessage()函數
                    curStateInfo = curStateInfo.parentStateInfo;//如果當前狀態沒有處理,則將父狀態設為當前狀態,繼續父狀態的ProcessMessage函數處理
                    if (curStateInfo == null) {//如果不再有父狀態了,則作為未處理的信息打印Log
                        mSm.unhandledMessage(msg);
                        break;
                    }
                    if (mDbg) {
                        Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
                    }
                }

狀態機中的狀態,可能會有父狀態,如果當前狀態的processMessage函數返回false 或者 NOT_HANDLED,就會向上調用父狀態的processMessage函數進行處理,如果最頂端的父狀態也沒處理,那就交給unhandledMessage函數做最後的處理(一般是丟掉,當然可以自己定義最後的處理函數)。隨後

當所有的處理結束後,狀態機可以調用transitionToHaltingState進入HaltingState(StateMachine內部預設的狀態)。並調用到自定義StateMachine的onHalting()函數,進入HaltingState狀態後,所有隨後發來的消息,都會導致HaltingState的haltedProcessMessage的調用(同樣需要繼承實現自定義處理)。

如果想要停止狀態機,可以調用quit或者abort方法,從而進入QuittingState,並在下一次處理時,退出HandlerThread線程,清理內部各個對象。

狀態的轉換會導致當前狀態的退出,和新狀態的進入,當從當前狀態退出時,會逐層向上調用父狀態的退出exit函數,但注意,這種逐層調用,會在當前狀態和目標狀態的共同父狀態處不再執行exit(),如果前狀態和目標狀態的不存在共同的父狀態,則徹底退出當前狀態的所有父狀態,並進入新狀態。

        private final void invokeExitMethods(StateInfo commonStateInfo) {//commonStateInfo是前狀態和目標狀態的共同父狀態
            while ((mStateStackTopIndex >= 0) &&
                    (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
                State curState = mStateStack[mStateStackTopIndex].state;
                if (mDbg) Log.d(TAG, "invokeExitMethods: " + curState.getName());
                curState.exit();
                mStateStack[mStateStackTopIndex].active = false;
                mStateStackTopIndex -= 1;
            }
        }

狀態機還可以調用deferMessage方法和sendMessageAtFrontOfQueue方法。
deferMessage方法會將該消息保存在一個延遲隊列中,這時並不發送出去,而是會在下一次狀態轉變的時候(例如從A狀態變為B狀態),將延遲隊列中的所有消息放在消息隊列的最前面。這些消息就會在B狀態作為當前狀態時被處理。
sendMessageAtFrontOfQueue方法會調用狀態機的Handler的sendMessageAtFrontOfQueue()方法,將當前發送的消息,排在消息隊列的最前面而不是原本的最後面。
為了說明這些特性,下面是一個具有8個狀態的狀態層次。

          mP0
         /   \
        mP1   mS0
       /   \
      mS2   mS1
     /  \    \
    mS3  mS4  mS5  ---> 初始狀態

當狀態機開始後,進入初始狀態mS5,各個父狀態同樣也是活動的,於是mP0, mP1, mS1 和mS5都是活動的。當有一個消息發出來,就會依次調用mS5,
mS1, mP1, mP0的processMessage方法(前提是都會返回false或者NOT_HANDLED)。
然後現在假設mS5的processMessage可以處理這個消息,並且會調用transitionTo(mS4)將狀態轉為mS4,然後返回true 或 HANDLED。processMessage返回後會進入performTransitions方法,其會找到mS5和mS4的共同父狀態,也就是mP1。緊接著會依次調用mS5.exit, mS1.exit 然後是 mS2.enter mS4.enter. 這時mP0, mP1, mS2,mS4 這四個狀態是活動的,當下一個消息到來的時候,就會激活mS4.processMessage方法。

下面是一個繼承了StateMachine的HelloWorld。該狀態機會在接收每一個消息的時候,打印一個 “Hello World” 字符串。

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 {
        &#64;Override public boolean processMessage(Message message) {
            Log.d(TAG, "Hello World");
            return HANDLED;
        }
    }
    State1 mState1 = new State1();
}
void testHelloWorld() {
    HelloWorld hw = makeHelloWorld();
    hw.sendMessage(hw.obtainMessage());
}

下面是一個具有4個狀態的狀態機,並分為2個獨立的父狀態

        mP1      mP2
       /   \
      mS2   mS1--初始狀態

下面是這幾個狀態的偽代碼

state mP1 {
     enter { log("mP1.enter"); }
     exit { log("mP1.exit");  }
     on msg {
         CMD_2 {
             send(CMD_3);
             defer(msg);
             transitonTo(mS2);
             return HANDLED;
         }
         return NOT_HANDLED;
     }
}


state mS1 parent mP1 {
     enter { log("mS1.enter"); }
     exit  { log("mS1.exit");  }
     on msg {
         CMD_1 {
             transitionTo(mS1);
             return HANDLED;
         }
         return NOT_HANDLED;
     }
}


state mS2 parent mP1 {
     enter { log("mS2.enter"); }
     exit  { log("mS2.exit");  }
     on msg {
         CMD_2 {
             send(CMD_4);
             return HANDLED;
         }
         CMD_3 {
             defer(msg);
             transitionTo(mP2);
             return HANDLED;
         }
         return NOT_HANDLED;
     }
}


state mP2 {
     enter {
         log("mP2.enter");
         send(CMD_5);
     }
     exit { log("mP2.exit"); }
     on msg {
         CMD_3, CMD_4 { return HANDLED; }
         CMD_5 {
             transitionTo(HaltingState);
             return HANDLED;
         }
         return NOT_HANDLED;
     }
}

測試代碼:

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");


        // 添加狀態
        addState(mP1);
            addState(mS1, mP1);
            addState(mS2, mP1);
        addState(mP2);


        // 設定初始狀態
        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();
}




//注意:添加synchronize塊是因為我們使用了hsm.wait()。
Hsm1 hsm = makeHsm1();//創建StateMachine對象
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

畫一個流程圖

狀態機的實例–DataConnection

Android源碼中使用狀態機的地方不少,比如Wifi狀態,數據連接狀態,藍牙耳機狀態等,我們取比較典型的Telephony中的DataConnection(Android4.2.2) 為例說明狀態機的使用。
這裡寫圖片描述

其中DcDefaultState是所有狀態的父狀態,<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjx0YWJsZT4NCjx0aGVhZD4NCgk8dHI+DQoJPHRoPg0KCQnXtMysPC90aD4NCgk8dGg+DQoJCbqs0uU8L3RoPg0KCTwvdHI+DQo8L3RoZWFkPg0KPHRib2R5Pg0KCTx0cj4NCgk8dGQ+RGNJbmFjdGl2ZVN0YXRlPC90ZD4NCgk8dGQ+t8e77rav17TMrDwvdGQ+DQoJPC90cj4NCgk8dHI+DQoJPHRkPkRjQWN0aXZhdGluZ1N0YXRlPC90ZD4NCgk8dGQ+vKS77te0zKw8L3RkPg0KCTwvdHI+DQoJPHRyPg0KCTx0ZD5EY0FjdGl2ZVN0YXRlPC90ZD4NCgk8dGQ+u+62r9e0zKw8L3RkPg0KCTwvdHI+DQoJPHRyPg0KCTx0ZD5EY0Rpc2Nvbm5lY3RpbmdTdGF0ZTwvdGQ+DQoJPHRkPsilvKS77te0zKw8L3RkPg0KCTwvdHI+DQoJPHRyPg0KCTx0ZD5EY0Rpc2Nvbm5lY3Rpb25FcnJvckNyZWF0aW5nQ29ubmVjdGlvbjwvdGQ+DQoJPHRkPrS0vajBrL3TyrGz9rTt17TMrDwvdGQ+DQoJPC90cj4NCjwvdGJvZHk+DQo8L3RhYmxlPg0KPGgxIGlkPQ=="狀態轉換圖">狀態轉換圖

這裡寫圖片描述

初始化

    protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm,
            DataConnectionTracker dct) {
            ......  
        addState(mDefaultState);
            addState(mInactiveState, mDefaultState);
            addState(mActivatingState, mDefaultState);
            addState(mActiveState, mDefaultState);
            addState(mDisconnectingState, mDefaultState);
            addState(mDisconnectingErrorCreatingConnection, mDefaultState);
        setInitialState(mInactiveState);
        ....

最開始處在DcInactiveState狀態,當DataConnectionTracker調用DataConnection的bringUp方法時

    public void bringUp(Message onCompletedMsg, ApnSetting apn) {
        sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
    }

會發送EVENT_CONNECT消息,這就會調用到當前狀態DcInactiveState的processMessage方法。

        public boolean processMessage(Message msg) {
            boolean retVal;


            switch (msg.what) {
                case EVENT_CONNECT:
                    ConnectionParams cp = (ConnectionParams) msg.obj;
                    cp.tag = mTag;
                    if (DBG) {
                        log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = "
                                + mRefCount);
                    }
                    mRefCount = 1;
                    onConnect(cp); //調用phone.mCM.setupDataCall 方法想RILJ發送RIL_REQUEST_SETUP_DATA_CALL請求。
                    transitionTo(mActivatingState);//切換到mActivatingState狀態。
                    retVal = HANDLED;
                    break;

其他狀態的切換的處理根據需求和興趣自行閱讀即可。

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