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是所有状态的父状态,

状态 含义
DcInactiveState 非活动状态
DcActivatingState 激活状态
DcActiveState 活动状态
DcDisconnectingState 去激活状态
DcDisconnectionErrorCreatingConnection 创建连接时出错状态

状态转换图

技术分享

初始化

    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;

其他状态的切换的处理根据需求和兴趣自行阅读即可。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。