先说两句题外话,很感谢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方法,第三个执行状态转移,即更新状态树。
从这段代码中(!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类,其构造方法中,关于状态机的代码如下:
在Android系统中,经常使用状态机来处理不同状态下的行为动作。状态机是将对象的状态与行为封装在一起;可以解决庞大的分支语句带来程序阅读性差和不便于进行扩展问题,使整个结构变得更加清晰明了,降低程序管理的复杂性提高灵活度。Android系统的StateMachine机制是一个State模式的应用,StateMachine是一个分层处理消息的状态机,并且是能够有分层排列状态。
构造状态机
StateMachine的构造函数都是protected类型,不能实例化,都是由其子类进行初始化操作。StateMachine有两个重载的构造函数,一个是通过指定消息循环对象来构造状态机
- protected StateMachine(String name, Looper looper) {
- initStateMachine(name, looper);
- }
另一个则是直接启动一个消息循环线程来构造一个状态机
- protected StateMachine(String name) {
-
- mSmThread = new HandlerThread(name);
- mSmThread.start();
- Looper looper = mSmThread.getLooper();
- initStateMachine(name, looper);
- }
这两种构造函数都会通过initStateMachine函数来初始化该状态机
- private void initStateMachine(String name, Looper looper) {
- mName = name;
- mSmHandler = new SmHandler(looper, this);
- }
初始化过程比较简单,就是将状态机名称保存到成员变量mName中,同时创建SmHandler对象,SmHandler是一个Handle对象,用于派发消息。
状态机中的每个状态使用State来封装,对于每个状态的信息又采用StateInfo来描述
StateMachine三个内部类:
1.ProcessedMessageInfo:保存已处理消息的信息;
2.ProcessedMessages:存储StateMachine最近处理的一些消息,需要保存最近处理的消息条数默认20,可以用户自己设定最大数目;
3.SmHandle是消息处理派发和状态控制切换的核心,运行在单独的线程上;
SmHandle成员变量定义:
ProcessedMessages用于保存已处理过的消息,mStateStack和mTempStateStack是一个数组栈,用于保存状态机中的链式状态关系。
mStateInfo定义为一个Hash链表,用于保存添加的所有状态。mInitialState保存初始状态,mDestState保存切换的目的状态。
建立树形层次结构状态机
在构造完一个状态机前需要向状态机中添加各种状态,StateMachine提供了addState函数来添加状态
- protected final void addState(State state, State parent) {
- mSmHandler.addState(state, parent);
- }
直接通过mSmHandler来完成状态添加过程
- private final StateInfo addState(State state, State parent) {
- if (mDbg) {
- Log.d(TAG, "addStateInternal: E state=" + state.getName()
- + ",parent=" + ((parent == null) ? "" : parent.getName()));
- }
- StateInfo parentStateInfo = null;
- if (parent != null) {
- parentStateInfo = mStateInfo.get(parent);
- if (parentStateInfo == null) {
-
- parentStateInfo = addState(parent, null);
- }
- }
- StateInfo stateInfo = mStateInfo.get(state);
- if (stateInfo == null) {
- stateInfo = new StateInfo();
- mStateInfo.put(state, stateInfo);
- }
-
-
- if ((stateInfo.parentStateInfo != null) &&
- (stateInfo.parentStateInfo != parentStateInfo)) {
- throw new RuntimeException("state already added");
- }
- stateInfo.state = state;
- stateInfo.parentStateInfo = parentStateInfo;
- stateInfo.active = false;
- if (mDbg) Log.d(TAG, "addStateInternal: X stateInfo: " + stateInfo);
- return stateInfo;
- }
状态添加过程其实就是为每个State创建相应的StateInfo对象,通过该对象来建立各个状态之间的关系,并且一个State-StateInfo键值对的方式保存到mStateInfo Hash表中。StateInfo就是包装State组成一个Node,建立State的父子关系;mStateInfo =new HashMap<State, StateInfo>();用来保存State Machine中的所有State,可以按照树形层次结构组织状态机中的所有状态
接下来介绍构造以下树形状态机的过程:
- sm.addState(S0,null);
- sm.addState(S1,S0);
- sm.addState(S2,S0);
- sm.addState(S3,S1);
- sm.addState(S4,S1);
- sm.addState(S5,S2);
- sm.addState(S6,S2);
- sm.addState(S7,S2);
- setInitialState(S4);
1.添加根节点状态过程
对于根节点状态的加入sm.addState(S0,null)代码执行如下:
- private final StateInfo addState(S0,null)
- {
- StateInfo parentStateInfo = null;
-
- StateInfo stateInfo = mStateInfo.get(state);
- if (stateInfo == null) {
-
- stateInfo = new StateInfo();
-
- mStateInfo.put(state, stateInfo);
- }
-
- stateInfo.state = state;
-
- stateInfo.parentStateInfo = parentStateInfo;
- stateInfo.active = false;
- return stateInfo;
- }
2.添加树枝节点状态过程
对于子节点S1状态的加入过程如下sm.addState(S1,S0):
- private final StateInfo addState(State state, State parent)
- {
- StateInfo parentStateInfo = null;
-
- if (parent != null) {
-
- parentStateInfo = mStateInfo.get(parent);
-
- if (parentStateInfo == null) {
-
- parentStateInfo = addState(parent, null);
- }
- }
-
- StateInfo stateInfo = mStateInfo.get(state);
- if (stateInfo == null) {
-
- stateInfo = new StateInfo();
-
- mStateInfo.put(state, stateInfo);
- }
-
- if ((stateInfo.parentStateInfo != null) &&
- (stateInfo.parentStateInfo != parentStateInfo)) {
- throw new RuntimeException("state already added");
- }
-
- stateInfo.state = state;
-
- stateInfo.parentStateInfo = parentStateInfo;
- stateInfo.active = false;
- return stateInfo;
- }
对于其他树枝节点的添加过程类似,这里不在介绍,最后保存在mStateInfo表中的所有状态之间就建立了以下树形关系:
启动状态机
当向状态机中添加完所有状态时,通过函数start函数来启动状态机
- public void start() {
-
- if (mSmHandler == null) return;
-
- mSmHandler.completeConstruction();
- }
调用mSmHandler的completeConstruction函数来完成状态机的构造完成处理
- private final void completeConstruction() {
- if (mDbg) Log.d(TAG, "completeConstruction: E");
-
- int maxDepth = 0;
- for (StateInfo si : mStateInfo.values()) {
- int depth = 0;
-
- for (StateInfo i = si; i != null; depth++) {
- i = i.parentStateInfo;
- }
- if (maxDepth < depth) {
- maxDepth = depth;
- }
- }
- if (mDbg) Log.d(TAG, "completeConstruction: maxDepth=" + maxDepth);
-
- mStateStack = new StateInfo[maxDepth];
- mTempStateStack = new StateInfo[maxDepth];
-
- setupInitialStateStack();
-
- sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
- if (mDbg) Log.d(TAG, "completeConstruction: X");
- }
计算状态树的最大深度方法:
1.遍历状态树中的所有节点;
2.以每一个节点为起始点,根据节点父子关系查找到根节点;
3.累计起始节点到根节点之间的节点个数;查找最大的节点个数。
根据查找到的树的最大节点个数来创建两个状态堆栈,并调用函数setupInitialStateStack来填充该堆栈
- private final void setupInitialStateStack() {
-
- StateInfo curStateInfo = mStateInfo.get(mInitialState);
-
- for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
- mTempStateStack[mTempStateStackCount] = curStateInfo;
- curStateInfo = curStateInfo.parentStateInfo;
- }
-
- mStateStackTopIndex = -1;
-
- moveTempStateStackToStateStack();
- }
从上图可以看出,当初始状态为S4时,保存到mTempStateStack的节点为:
mTempStateStack={S4,S1,S0}
mTempStateStackCount = 3;
mStateStackTopIndex = -1;
然后调用函数moveTempStateStackToStateStack将节点以反序方式保存到mStateStack中
- 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;
- return startingIndex;
- }
mStateStack={S0,S1,S4}
mStateStackTopIndex = 2
初始化完状态栈后,SmHandler将向消息循环中发送一个SM_INIT_CMD消息
- sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj))
该消息对应的处理如下:
- else if (!mIsConstructionCompleted &&(mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
- mIsConstructionCompleted = true;
- invokeEnterMethods(0);
- }
- performTransitions();
消息处理过程首先调用invokeEnterMethods函数将mStateStack栈中的所有状态设置为激活状态,同时调用每一个状态的enter()函数
- 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;
- }
- }
最后调用performTransitions函数来切换状态,同时设置mIsConstructionCompleted为true,表示状态机已经启动完成,SmHandler在以后的消息处理过程中就不在重新启动状态机了。
状态切换
SmHandler在处理每个消息时都会调用performTransitions来检查状态切换
- private synchronized void performTransitions() {
- while (mDestState != null){
-
-
-
-
- StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
-
-
- invokeExitMethods(commonStateInfo);
-
- int stateStackEnteringIndex = moveTempStateStackToStateStack();
-
- invokeEnterMethods(stateStackEnteringIndex);
-
- moveDeferredMessageAtFrontOfQueue();
- }
- }
首先介绍一下状态切换的思路:
以上图中,初始状态为S4,现在目标状态mDestState被设置为S7。前面介绍了保存在mStateStack数组中的节点为:
mStateStack={S0,S1,S4}
mStateStackTopIndex = 2
这是以初始状态节点为起点遍历节点树得到的节点链表。
现在要切换到S7状态节点,则以S7为起始节点,同样遍历状态节点树,查找未激活的所有节点,并保存到mTempStateStack数组中
mTempStateStack={S7,S2,S0}
mTempStateStackCount = 3
接着调用mStateStack中除S0节点外的其他所有节点的exit函数,并且将每个状态节点设置为未激活状态,因此S4,S1被设置为未激活状态;将切换后的状态节点链表mTempStateStack移动到mStateStack,
mStateStack={S0,S2,S7}
mStateStackTopIndex = 2
并调用节点S2,S7的enter函数,同时设置为激活状态。
理解了整个状态切换过程后,就能更好地理解代码,首先根据目标状态建立状态节点链路表
- private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
- mTempStateStackCount = 0;
- StateInfo curStateInfo = mStateInfo.get(destState);
- do {
- mTempStateStack[mTempStateStackCount++] = curStateInfo;
- if (curStateInfo != null) {
- curStateInfo = curStateInfo.parentStateInfo;
- }
- } while ((curStateInfo != null) && !curStateInfo.active);
- return curStateInfo;
- }
然后弹出mStateStack中保存的原始状态
- private final void invokeExitMethods(StateInfo 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;
- }
- }
将新建立的状态节点链表保存到mStateStack栈中
- 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;
- return startingIndex;
- }
初始化入栈的所有新状态,并设置为激活状态
- 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;
- }
- }
如何设置目标状态呢?StateMachine提供了transitionTo函数来切换状态
- protected final void transitionTo(IState destState) {
- mSmHandler.transitionTo(destState);
- }
该函数直接调用SmHandler的transitionTo函数来实现,SmHandler的transitionTo函数定义如下:
- private final void transitionTo(IState destState) {
- mDestState = (State) destState;
- if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());
- }
这里只是简单地设置了mDestState变量,并未真正更新状态栈
mStateStack,在前面介绍了SmHandler在每次处理消息时都会自动更新一次mStateStack,无论mDestState变量值是否改变。由此可知目标状态的设置与状态栈的更新是异步的。
消息处理
StateMachine处理的核心就是SmHandler,就是一个Handler,运行在单独线程中。Handler是用来异步处理派发消息,这里使用Handler管理各个状态,派发消息处理到各个状态中去执行。StateMachine提供了多个消息发送接口,通过这些接口函数可以将消息发送到SmHandler中。
- public final void sendMessage(int what) {
-
- if (mSmHandler == null) return;
- mSmHandler.sendMessage(obtainMessage(what));
- }
SmHandler将处理通过SmHandler发送的消息,处理过程如下:
- public final void handleMessage(Message msg) {
-
- mMsg = msg;
- if (mIsConstructionCompleted) {
-
- processMsg(msg);
- } else if (!mIsConstructionCompleted &&
- (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
-
- mIsConstructionCompleted = true;
- invokeEnterMethods(0);
- } else {
- throw new RuntimeException("StateMachine.handleMessage: " +
- "The start method not called, received msg: " + msg);
- }
-
- performTransitions();
- }
变量mIsConstructionCompleted在状态机启动完成后被设置为true,因此这里将调用processMsg函数来完成消息处理。
- private final void processMsg(Message msg) {
- StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
-
- while (!curStateInfo.state.processMessage(msg)) {
-
- curStateInfo = curStateInfo.parentStateInfo;
- if (curStateInfo == null) {
-
- mSm.unhandledMessage(msg);
- if (isQuit(msg)) {
- transitionTo(mQuittingState);
- }
- break;
- }
- }
-
- if (mSm.recordProcessedMessage(msg)) {
- if (curStateInfo != null) {
- State orgState = mStateStack[mStateStackTopIndex].state;
- mProcessedMessages.add(msg, mSm.getMessageInfo(msg), curStateInfo.state,orgState);
- } else {
- mProcessedMessages.add(msg, mSm.getMessageInfo(msg), null, null);
- }
- }
- }
消息处理过程是从mStateStack栈顶派发到栈底,直到该消息被处理!