Android中的设计模式-状态模式
来源:互联网 发布:appium ios python 编辑:程序博客网 时间:2024/06/03 22:57
原文出处:http://www.linuxidc.com/Linux/2015-04/116013.htm
状态模式说明
“状态模式允许一个对象在其内部状态改变的时候改变其行为。这个对象看上去就像是改变了它的类一样。” –《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 { @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 ED/hsm1 ( 1999): ctor ED/hsm1 ( 1999): ctor XD/hsm1 ( 1999): mP1.enterD/hsm1 ( 1999): mS1.enterD/hsm1 ( 1999): makeHsm1 XD/hsm1 ( 1999): mS1.processMessage what=1D/hsm1 ( 1999): mS1.exitD/hsm1 ( 1999): mS1.enterD/hsm1 ( 1999): mS1.processMessage what=2D/hsm1 ( 1999): mP1.processMessage what=2D/hsm1 ( 1999): mS1.exitD/hsm1 ( 1999): mS2.enterD/hsm1 ( 1999): mS2.processMessage what=2D/hsm1 ( 1999): mS2.processMessage what=3D/hsm1 ( 1999): mS2.exitD/hsm1 ( 1999): mP1.exitD/hsm1 ( 1999): mP2.enterD/hsm1 ( 1999): mP2.processMessage what=3D/hsm1 ( 1999): mP2.processMessage what=4D/hsm1 ( 1999): mP2.processMessage what=5D/hsm1 ( 1999): mP2.exitD/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; }}
其他状态的切换的处理根据需求和兴趣自行阅读即可。
- Android中的设计模式-状态模式
- Android中的设计模式-状态模式
- android 设计模式 状态模式
- Android设计模式-状态模式
- Dota中的设计模式--状态模式
- android设计模式之---状态模式
- Android设计模式应用--状态模式
- Android 设计模式 笔记 - 状态模式
- Android 设计模式实战笔记 状态模式
- Android设计模式之状态模式
- android设计模式之状态模式
- Android设计模式(七)-状态模式
- Android实战设计模式-----状态模式
- Android设计模式之状态模式
- Android 设计模式之状态模式
- Android的设计模式-状态模式
- android 中的设计模式
- Android中的设计模式
- 王学岗RxJava(十五)——map,flatmap等交换方法
- android 值得收藏的demo 以及 地址
- mysql还原数据库遇到Unknown command 错误
- 1622-5 孔富晨《2016年11月13日》 【连续第44天总结】
- 数据结构(二)——单链表的概念和读取元素
- Android中的设计模式-状态模式
- J2EE十三种技术规范
- 【Day49】SQL注入攻击
- Linux开发工具学习笔记2
- 基于标记的AR的opencv实现(一)
- iOS开发中的Assets.xcassets和Assets.car
- js图片预加载和图片懒加载
- git学习
- 如何从excel的多行中随机选出n行