Android StateMachine学习

2018年01月06日14:33:58,西安下了三天的大雪了,上班上学上街都不方便,索性在家看看代码吧,已经好久没有更新博客了,是因为之前的笔记本配置实在跟不上了,2018年元旦,狠下心来新买了一个某米的笔记本,除了存储256G不够使用(单单下载一套Android代码,编译的时候就没有空间了,可怜的只剩下40M了,只是中通快递很不给力,扩展的硬盘还在路上。。。。。)废话不多说了,下面进入正题

此次关于StateMachine,我打算从下面几点来学习:

  1. StateMachine中的常用方法介绍
  2. StateMachine结论
  3. StateMachine栗子
  4. StateMachine源码解析

StateMachine是Android中基于Handler消息机制封装的一个针对不同状态不同消息可以做出不同的处理行为的一个实现机制,一个继承自StateMachine的状态机,可以有在至少一个或者多个状态,并且需要设置初始状态,此时通过obtainMessage和sendMessage获取和发送的消息,默认就由当前的初始状态首先处理。

StateMachine中的常用方法介绍

addState  添加当前状态到状态树中

这里写图片描述
可以看到addState由两个重载,一个参数的是添加当前状态树的根节点,两个参数的是添加当前的状态,并且指定其父状态

setInitialState  指定当前状态树的初始状态

这里写图片描述

transitionTo  从当前状态切换到目的状态(需要注意的是,并不是立即切换的,这个后面看源码可以证明这一点)

这里写图片描述

deferMessage  暂时将当前消息保存到消息队列的顶端, 一旦切换到新的状态,首先处理该消息

这里写图片描述

sendMessageAtFrontOfQueue  

发送一个消息到需要处理的消息队列的顶部,我们知道当前接受到的消息首先放到一个队列里,之后从队列里首先取出顶部的消息来处理,所以sendMessageAtFrontOfQueue方法就是确保当前
发送的消息首先得以执行

这里写图片描述

另外每一个状态都是继承自 State的,所以还需要重点关注下 State的方法
这里写图片描述

enter  每次进入当前状态的时候执行的
exit   每次退出当前状态的时候执行的
processMessage   当前状态处理消息的方法
getName    获取当前状态的名称

StateMachine结论

StateMachine的构建过程

使用addState来构建一个StateMachine的树结构,另外通过setInitialState方法设置指定初始状态,之后通过start来启动该StateMachine,从根状态到初始状态路径下的的所有状态的enter方法首先得以执行,如下:

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

ms1为初始状态,则当前StateMachine执行enter的顺序为
mP1#enter---> mS1#enter
此时向当前StateMachine发来的消息由mS1#processMessage执行

消息处理

  • 当前StateMachine初始化完成以后,通过obtainMessage和sendMessage获取和发送消息,收到消息以后,首先是当前所处状态的processMessage执行,以上例子,就是ms1,另外ms1也可以通过transitionTo切换到新的状态,
    需要注意的是,transitionTo并不能立刻切换到新状态
  • 如果当前的所处的状态机不能处理消息,则在processMessage方法返回false,此时由其父状态来处理
  • 通过transitionTo切换到新的状态机时候,沿着当前的状态机到需要切换到的目的状态机的共同父状态依次的exit方法,然后顺着共同父亲状态依次执行新的状态机的enter方法(需要注意的是,公共父状态的enter和exit方法不会执行)
  • 如果想要停止状态机,可以调用quit或者abort方法,从而进入QuittingState,并在下一次处理时,退出HandlerThread线程,清理内部各个对象。
如下栗子:
          mP0
         /   \
        mP1   mS0
       /   \
      mS2   mS1
     /  \    \
    mS3  mS4  mS5  ---> initial state

对于上面的状态机启动以后,当前处于激活的状态有:mP0,mP1,mS1,mS5 由于初始状态是mS5,所以发过来的消息,首先由mS5处理,假如mS5和他的父状态都不能处理该消息,则依次返回false,依次执行下面状态机的processMessage
mS5–>mS1–>mP1–>mP0


还是上面的状态机,假如当前状态机mS5返回true并且处理了消息,并且调用transitionTo(mS4)切换到新的mS4状态,此时会依次执行如下方法:mS5.exit()–>mS1.exit()–>mS2.enter()–>mS4.enter(), 此时处于激活的状态如下:
mP0,mP1,mS2,mS4,由于当前的状态是mS4,所以此时发过来的消息由mS4来处理

StateMachine栗子(一)

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("Hello World");
            return HANDLED;
        }
    }
    State1 mState1 = new State1();
}

void testHelloWorld() {
    HelloWorld hw = makeHelloWorld();
    hw.sendMessage(hw.obtainMessage());
}

上面的demo中,在调用hw.start以后,状态树中只有一个mState1,并且初始状态也是mState1

此时打印如下:
Hello World

StateMachine栗子(二)

class Hsm1 extends StateMachine {
    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("makeHsm1 E");
        Hsm1 sm = new Hsm1("hsm1");
        sm.start();
        log("makeHsm1 X");
        return sm;
    }

    Hsm1(String name) {
        super(name);
        log("ctor E");

        // 构造状态树
        addState(mP1);
            addState(mS1, mP1);
            addState(mS2, mP1);
        addState(mP2);

        // 设置初始状态
        setInitialState(mS1);
        log("ctor X");
    }

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

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

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


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

    Override
    void onHalting() {
        log("halting");
        synchronized (this) {
            this.notifyAll();
        }
    }

    P1 mP1 = new P1();
    S1 mS1 = new S1();
    S2 mS2 = new S2();
    P2 mP2 = new P2();
}

============================================

// 下面是测试代码
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) {
          loge("exception while waiting " + e.getMessage());
     }
}

上面的代码构造出的状态树如下,mS1为初始状态

          mP1        mP2
         /   \
        mS1   mS2

消息的处理分析

下面一步一步分析消息的处理:

Hsm1 hsm = makeHsm1();  // 构造状态树
==================================
public static Hsm1 makeHsm1() {
        log("makeHsm1 E");
        Hsm1 sm = new Hsm1("hsm1");
        sm.start();
        log("makeHsm1 X");
        return sm;
}

Hsm1(String name) {
        super(name);
        log("ctor E");

        // 构造状态树
        addState(mP1);
            addState(mS1, mP1);
            addState(mS2, mP1);
        addState(mP2);

        // 设置初始状态, 此时等到当前状态机start以后,会执行从根状态到初始状态的所有enter方法
        setInitialState(mS1);
        log("ctor X");
    }


打印如下:
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

上面构建树的动作完成以后,处于激活状态的状态如下:
mP1  和  mS1
  • 发送CMD_1消息
hsm.sendMessage(obtainMessage(hsm.CMD_1));   // 发送CMD_1消息
===========================================

由于当前所处的状态就是初始状态mS1,所以CMD_1由其来处理, 因为mS1已经处于激活状态,所以其enter方法将不会执行,直接由processMessage来处理消息
 Override 
 public boolean processMessage(Message message) {
            log("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;
            }
  }

上面mS1对于CMD_1消息处理,又切换到了当前状态,所以又会走一次其exit和enter方法:
D/hsm1    ( 1999): mS1.processMessage what=1
D/hsm1    ( 1999): mS1.exit
D/hsm1    ( 1999): mS1.enter
  • 发送CMD_2消息
hsm.sendMessage(obtainMessage(hsm.CMD_2));
=============================================
上面CMD_1消息处理完成以后,当前状态还处于mS1, 所以对于CMD_2消息,还是由当前状态mS1来处理,可以看到mS1无法处理CMD_2消息,所以由其父状态来处理

下面是P1处理消息的方法
public boolean processMessage(Message message) {
            boolean retVal;
            log("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;
}
可以看到在P1状态机中处理CMD_2消息,会先将当前CMD_2消息defer出去,同时send了一个CMD_3消息, 并且切换到了mS2状态, 这里需要重点注意当切换到了mS2以后,会首先处理之前defer出的消息,之后才会处理send过来的消息,另外由于切换状态到了mS2,又由于mS2之前没有启动过,所以会优先执行mS1的exit方法和mS2的enter方法,所以log打印如下:

// 由当前mS1无法处理CMD_2消息所以由其父状态mP1处理
D/hsm1    ( 1999): mS1.processMessage what=2
D/hsm1    ( 1999): mP1.processMessage what=2
// 由于切换到了mS2,所以依次执行 mS1.exit和mS2.enter 方法
D/hsm1    ( 1999): mS1.exit
D/hsm1    ( 1999): mS2.enter

下面S2处理消息:
Override 
public boolean processMessage(Message message) {
            boolean retVal;
            log("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;
}
上一步,在P1状态中,defer了CMD_2消息,并且send了一个CMD_3消息,此时log如下:
// 首先处理CMD_2,  sendMessage(obtainMessage(CMD_4));
D/hsm1    ( 1999): mS2.processMessage what=2   
// 再处理CMD_3消息,  deferMessage(CMD_3);  transitionTo(mP2);
D/hsm1    ( 1999): mS2.processMessage what=3   

同样的道理,切换到mP2状态, 由于mP2和mS2处于不同的树中,因此需要完全退出当前的状态树,同时进入新的状态树的mP2的状态树中,此时log如下:
D/hsm1    ( 1999): mS2.exit
D/hsm1    ( 1999): mP1.exit
D/hsm1    ( 1999): mP2.enter

P2状态处理消息如下:
Override 
public void enter() {
            log("mP2.enter");
            sendMessage(obtainMessage(CMD_5));
}
Override 
public boolean processMessage(Message message) {
            log("P2.processMessage what=" + message.what);
            switch(message.what) {
            case(CMD_3):
                break;
            case(CMD_4):
                break;
            case(CMD_5):
                transitionToHaltingState();
                break;
            }
            return HANDLED;
}
此时log如下:
D/hsm1    ( 1999): mP2.processMessage what=3  // 首先处理CMD_3
D/hsm1    ( 1999): mP2.processMessage what=4  // 再处理CMD_4消息
D/hsm1    ( 1999): mP2.processMessage what=5
D/hsm1    ( 1999): mP2.exit
D/hsm1    ( 1999): halting

StateMachine源码解析

到现在为止,我们已经知道了自己继承自StateMachine的一个状态机的构造过程和发送消息,的处理流程,如下:

super(name)  // 调用父类的构造方法
addState(父状态)
   addState(子状态,父状态)

setInitialState(初始状态)

hm.start()  //启动状态机

hsm.sendMessage(obtainMessage(hsm.CMD_1));  // 发送消息

那么源码的分析当然也是跟着这个走的

StateMachine初始化

StateMachine初始化构造HandlerThread开启消息循环的初始化
在我们自己的状态机的构造方法里,都要调用super(name)来调用父类的构造方法,在这里会提前完成一些handler,和handlerThread的消息循环的初始化
这里写图片描述
这里写图片描述
上面可以看出其实在StateMachine内部消息驱动也是通过handler和HandlerThread来实现的,其内部由一个mSmHandler继承自Handler用来分发传递过来的消息

构造状态树addState

构造状态树addState

private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();

private final StateInfo addState(State state, State parent) {
            StateInfo parentStateInfo = null;
            // 对当前的父状态做检查,若当前状态的父亲状态没有提前添加到状态树中,则首先添加其父状态
            if (parent != null) {
                parentStateInfo = mStateInfo.get(parent);
                if (parentStateInfo == null) {
                    // Recursively add our parent as it's not been added yet.
                    parentStateInfo = addState(parent, 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;
}

以上可以看出,系统添加状态的时候,会将所有状态添加到一个mStateInfo MAP集合中去,并且确保当前的父状态由于于子状态提前添加到状态树中去。

设置初始状态setInitialState

设置初始状态setInitialState

private final void setInitialState(State initialState) {
            mInitialState = initialState;
}

上面设定初始状态比较简单,只是暂时指定了当前的initialState为初始状态

启动状态机start

启动状态机start

以上其实并不是真正的构造完当前的状态树,而是先临时将当前的状态和其父状态的关系通过mStateInfo集合保存起来,设定初始状态也是一样,先通过mInitialState属性来保存当前的初始状态是哪一个而已,真正构造起来整个状态树其实是在启动当前状态机start的时候完成的

这里写图片描述

completeConstruction

completeConstruction计算当前所有树最深的深度,根据该深度构造两个相同大小的数组,将从根状态开始,从父状态到初始状态的所有状态保存到mStateStack数组中,最终发送*SM_INIT_CMD消息,依次执行mStateStack数组中的状态的enter方法

private final void completeConstruction() {

            int maxDepth = 0;
            // 遍历addState时候加入mStateInfo的所有状态
            Iterator i$ = this.mStateInfo.values().iterator();

            while(i$.hasNext()) {
                StateMachine.SmHandler.StateInfo si = (StateMachine.SmHandler.StateInfo)i$.next();
                int depth = 0;
                // 计算出当前树中的最深层次数
                for(StateMachine.SmHandler.StateInfo i = si; i != null; ++depth) {
                    i = i.parentStateInfo;
                }
                // 由于可能存在多个状态树,最终取层次最深的树的深度作为最终深度
                if(maxDepth < depth) {
                    maxDepth = depth;
                }
            }
            // 根据当前状态机最深层次深度构造两个数组
            this.mStateStack = new StateMachine.SmHandler.StateInfo[maxDepth];
            this.mTempStateStack = new StateMachine.SmHandler.StateInfo[maxDepth];
            this.setupInitialStateStack();  // 构造状态树
            this.sendMessageAtFrontOfQueue(this.obtainMessage(SM_INIT_CMD, mSmHandlerObj));

}

setupInitialStateStack

保存添加初始状态和其父状态到mTempStateStack数组

private final void setupInitialStateStack() {
            // 获取到当前的初始状态对应的StateInfo
            StateMachine.SmHandler.StateInfo curStateInfo = (StateMachine.SmHandler.StateInfo)this.mStateInfo.get(this.mInitialState);
            // 填充临时的mTempStateStack数组,保存当前处于激活状态的所有状态,初始状态索引从0开始,后面依次是其父状态
            for(this.mTempStateStackCount = 0; curStateInfo != null; ++this.mTempStateStackCount) {
                this.mTempStateStack[this.mTempStateStackCount] = curStateInfo;
                curStateInfo = curStateInfo.parentStateInfo;
            }

            this.mStateStackTopIndex = -1;
            this.moveTempStateStackToStateStack();
}

上面,填充临时的mTempStateStack数组,保存当前处于激活状态的所有状态,初始状态索引从0开始,后面依次是其父状态,下面举个栗子

          mP1        mP2
         /   \
        mS1   mS2

        addState(mP1);
            addState(mS1, mP1);
            addState(mS2, mP1);
        addState(mP2);
        setInitialState(mS1);

以上面的状态树为栗子,mS1为初始状态,则当前对应的数据如下:

// mTempStateStack数组中存放的数据
mTempStateStack {mS1,mP1}
mTempStateStackCount = 2
mStateStackTopIndex = -1
moveTempStateStackToStateStack

moveTempStateStackToStateStack拷贝反转临时数组到mStateStack
moveTempStateStackToStateStack方法将上一步mTempStateStack保存的临时数据复制到 mStateStack中,并将其索引反转过来

private final int moveTempStateStackToStateStack() {
            int startingIndex = mStateStackTopIndex + 1;  // startingIndex = 0
            int i = mTempStateStackCount - 1;  // mTempStateStack数组的最后一个数据索引
            int j = startingIndex;
            // 将mTempStateStack中的数据复制到mStateStack中,并将其索引反转过来
            while (i >= 0) {
                mStateStack[j] = mTempStateStack[i];
                j += 1;
                i -= 1;
            }

            mStateStackTopIndex = j - 1;  // 保存当前mStateStack数组中最后一个状态(也就是初始状态)的索引
            return startingIndex;  // startingIndex = 0
}

还是上面的状态树,moveTempStateStackToStateStack完成之后,数据如下:

mStateStack数组的数据:
mStateStack = {mP1,mS1}
mStateStackTopIndex = 1

到现在为止,当前从初始状态所在树的树根到初始状态都已经按照顺序保存到了mStateStack数组中,好了,现在回到completeConstruction方法,继续执行,会发送SM_INIT_CMD消息给SmHandler处理

// 从根状态到初始状态,依次执行每个状态的enter方法
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));

SmHandler#handleMessage

SmHandler#handleMessage构造完数组发送消息

@Override
        public final void handleMessage(Message msg) {
            if (!mHasQuit) {
                ....
                State msgProcessedState = null;
                if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) {
                    /** Normal path */
                    msgProcessedState = processMsg(msg);
                } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
                        && (mMsg.obj == mSmHandlerObj)) {
                    /** Initial one time path. */
                    mIsConstructionCompleted = true;
                    invokeEnterMethods(0);
                } 
                ....
            }
        }

处理SM_INIT_CMD消息,执行invokeEnterMethods(0);方法依次调用enter方法

invokeEnterMethods执行enter方法

private final void invokeEnterMethods(int stateStackEnteringIndex) {
            for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
                if (stateStackEnteringIndex == mStateStackTopIndex) {
                    // Last enter state for transition
                    mTransitionInProgress = false;
                }
                mStateStack[i].state.enter();
                mStateStack[i].active = true;
            }
            mTransitionInProgress = false; // ensure flag set to false if no methods called
        }

在上面构造状态树,将当前从根状态到初始状态保存到mStateStack 数组中,可以知道mStateStackTopIndex就是该mStateStack数组的长度,并且该数组从前往后,依次是从根状态到开始,从父状态到子状态,如下:

          mP1        mP2
         /   \
        mS1   mS2
mS1为初始状态
mStateStack = {mP1,mS1}

所以invokeEnterMethods其实就是从根状态开始,从父状态到子状态依次执行每个状态的enter方法。

到现在为止,自定义的StateMachine的初始化就完成了,下面继续消息的处理

SmHandler处理消息

发送一条消息
hsm.sendMessage(obtainMessage(hsm.CMD_1));

这里写图片描述
可以看到,其实状态机接收到的消息,还是给其内部的SmHandler去处理的

@Override
public final void handleMessage(Message msg) {
    ....
    if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) {
                    /** Normal path */
                    msgProcessedState = processMsg(msg);
    }
    ....

}

SmHandler#processMsg

private final State processMsg(Message msg) {
            // 从mStateStack获取当前的状态
            StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
            if (isQuit(msg)) {
                transitionTo(mQuittingState);
            } else {
                while (!curStateInfo.state.processMessage(msg)) { //当前状态处理消息,如果不能处理返回false,则循环获取其父状态处理
                    /**
                     * Not processed
                     */
                    curStateInfo = curStateInfo.parentStateInfo;
                    if (curStateInfo == null) {
                        /**
                         * No parents left so it's not handled
                         */
                        // 如果当前状态没有父状态了,则该消息没有对应状态处理
                        mSm.unhandledMessage(msg);
                        break;
                    }
                }
            }
            return (curStateInfo != null) ? curStateInfo.state : null;
}

上面的processMsg也正好验证了,我们上面的结论,如果当前的状态无法处理消息,则由其父状态去处理

deferMessage处理

之前说了deferMessage是将该消息保存在一个延迟队列中,这时并不发送出去,而是会在下一次状态转变的时候,将延迟队列中的所有消息放在消息队列的最前面。这些消息就会在切换到新的状态时被优先处理。下面看代码来证明它
这里写图片描述

private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();

private final void deferMessage(Message msg) {

            /* Copy the "msg" to "newMsg" as "msg" will be recycled */
            Message newMsg = obtainMessage();
            newMsg.copyFrom(msg);

            mDeferredMessages.add(newMsg);
}

可以看到deferMessage其实就是先将消息拷贝一份并且添加到mDeferredMessages集合中去,下面看看transitionTo方法

transitionTo转换状态

这里写图片描述

这里写图片描述
这里可以验证我们上面所说的,transitionTo并不是立即切换到新的状态,其实是暂时把需要切换到的目的状态暂时保存起来,等待当前状态处理消息完成以后,才会进行切换,这点在handleMessage可以证实:
这里写图片描述
可以看到,在当前状态处理某一条消息期间,就算执行transitionTo也只是暂时将目的状态保存起来,后面等到该条消息处理完成之后,才会真正执行performTransitions去转换到目的状态

performTransitions


private void performTransitions(State msgProcessedState, Message msg) {
            /**
             * If transitionTo has been called, exit and then enter
             * the appropriate states. We loop on this to allow
             * enter and exit methods to use transitionTo.
             */
            State orgState = mStateStack[mStateStackTopIndex].state;


            State destState = mDestState;  // 获取transitionTo中保存的目的状态
            if (destState != null) {
                /**
                 * Process the transitions including transitions in the enter/exit methods
                 */
                while (true) {

                    /**
                     * Determine the states to exit and enter and return the
                     * common ancestor state of the enter/exit states. Then
                     * invoke the exit methods then the enter methods.
                     */
                    // 计算出目的状态和当前状态的公共父状态commonStateInfo
                    StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                    // flag is cleared in invokeEnterMethods before entering the target state
                    mTransitionInProgress = true;
                    // 依次执行从当前状态到公共父状态commonStateInfo的exit方法
                    invokeExitMethods(commonStateInfo);
                    // 将临时数组mTempStateStack中的数据反转赋值给mStateStack数组,并返回当前mStateStack数组的长度
                    int stateStackEnteringIndex = moveTempStateStackToStateStack();
                    // 从公共父状态依次执行enter方法,这里注意公共父状态是不执行enter的
                    invokeEnterMethods(stateStackEnteringIndex);

                    /**
                     * Since we have transitioned to a new state we need to have
                     * any deferred messages moved to the front of the message queue
                     * so they will be processed before any other messages in the
                     * message queue.
                     */
                    // 当前目的状态已经处于激活状态,此时优先处理mDeferredMessages集合中的消息
                    moveDeferredMessageAtFrontOfQueue();

                }
            }

         ...
}

setupTempStateStackWithStatesToEnter

计算出目的状态和当前状态的公共父状态commonStateInfo
这里写图片描述

invokeExitMethods

依次执行从当前状态到公共父状态commonStateInfo的exit方法
这里写图片描述

moveTempStateStackToStateStack

**将临时数组mTempStateStack中的数据反转赋值给mStateStack数组,并返回当前mStateStack数组的长度
**

这里写图片描述

invokeEnterMethods

从公共父状态依次执行enter方法,这里注意公共父状态是不执行enter的
这里写图片描述

moveDeferredMessageAtFrontOfQueue

当前目的状态已经处于激活状态,此时优先处理mDeferredMessages集合中的消息
这里写图片描述

从moveDeferredMessageAtFrontOfQueue方法可以看出,越是最后defer的message,越会优先被处理

好了,到现在为止,StateMachine的原理已经缕清了,下面我做一下总结:

总结

StateMachine流程

  • super(name) 构造SmHandler初始化消息循环
  • addState 添加当前的状态到mStateInfo集合
  • setInitialState 设置初始状态
  • start() 启动当前状态机,在start方法里完成了状态树的构建,并且计算当前所有树的最大深度,并且创建长度为最大深度的数组mStateStack,mStateStack中依次保存从根状态到当前状态的所有状态,并且当前状态在最后一个索引存放, 最后发送 消息,依次执行,从根状态到当前初始状态的enter方法,完成之后,通过performTransitions切换到初始状态
  • sendMessage 发送SM_INIT_CMD消息,最终是由当前状态的processMsg处理

方法总结

  • 对于当前状态机接收到的消息,首先由当前的状态处理,如果当前的状态不能处理该消息,则由其父状态去处理,如果所有的状态都不能处理,则会走到其父类StateMachine#unhandledMessage方法
  • deferMessage是将当前消息先临时存放到mDeferredMessages集合中,等到切换到新的状态,在依次从mDeferredMessages集合中取出消息,在新的状态处理,后defer的消息会优先被处理
  • transitionTo切换状态并不能立即切换到目的状态,而是现将目的状态保存起来,等到当前状态处理当前消息完成以后,才通过performTransitions切换到目的状态
  • 从当前状态切换到目的状态,首先会计算出当前状态和目的状态的公共父亲状态,然后,从当前状态到公共父亲状态依次执行exit方法,在从父亲状态到目的状态依次执行enter方法(需要注意的是,公共父亲状态不参与exit和enter方法的执行)

2018雪景大赛

最后晒晒最近朋友圈的各种大神的雪景大赛,也希望亲人和朋友2018事事顺心。。。
这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

猜你喜欢

转载自blog.csdn.net/mockingbirds/article/details/78988672
今日推荐