StateMachine 源码流程分析

1、概述。
          Android中对state模式的应用,通过StateMachine、state、Istate这几个类来共同实现。方便处理状态多变,分支庞大的条件语句。由于对state设计
   模式了解不深,此处不做探讨。只分析StateMachine的源码。
         下面通过使用步骤来分析state的源码。
 2、StateMachine分析
        2.1、初始化分析。
              使用StateMachine时,先建一个类继承 StateMachine.一般在这个类的构造中去添加状态机的树形结构和初始化状态机。如:

                                                    
             上述代码建立的状态机的树形结构图如下:
                                   
                                                            
               StateA、StateB、.... StateF都是继承State的类,代表一个状态。
               如:
                                            
                  
          重写 enter 、exit方法,在StateMachine进入、退出当前状态时调用。
          重写 processMessage方法,在当前状态下有消息发过来时,对消息的处理。
          addState()。方法用创建状态对应的Stateinfo,并通过Stateinfo的parentStateinfo、active等属性 来建立状           态机的 树形结构,有父状态就添加父状态。
           setInitialState():设置初始化的状态,赋值给SmHandler类的 成员变量:mInitialState。后面会判断它。
     2.2、启动状态机。
          通过上面的代码就完成了基本的状态机的初始化。接下来就是启动状态机了。 
                         
                      new 这个StateMachine的实例,传的参数用来打印Log。并且调用它的start();方法。
                                   
                        先判断SmHandler有没有实例化,这个类的重要性不言而喻。StateMachine里面的很多的方法的实现都是在这个Handler中。
                      
                      SmHandler.completeConstruction().
            private final void completeConstruction() {
                      ......
            int maxDepth = 0;
            //计算状态树的深度。
            for (StateInfo si : mStateInfo.values()) {
                int depth = 0;
                //根据StateInfo提供的父子关系进行计算。首先遍历所有的节点,然后以每一个节点为起始点,计算从起始点到根节点的
                //节点个数,找出最大值,即为树形结构的最大深度。此例子 maxDepth ==3。(有几层就是几个)
                for (StateInfo i = si; i != null; depth++) {
                    i = i.parentStateInfo;
                }
                if (maxDepth < depth) {
                    maxDepth = depth;
                }
            }
                      //建立树形图的状态链相关的栈,方便对树形结构的状态切换的控制。它们两个和组完成状态之间的切换。
            mStateStack = new StateInfo[maxDepth];//一般和 TempStateStack配合来控制那些状态需要调用enter等。
            mTempStateStack = new StateInfo[maxDepth]; //一般和StateStack配合来控制那些状态需要调用exit方法等。
            setupInitialStateStack();//初始化状态栈,根据 setInitialState设置的初始状态。
            sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));//发送消息告诉SmHandler。
              }

           SmHandler.completeConstruction().
           private final void setupInitialStateStack() {
           ......
            StateInfo curStateInfo = mStateInfo.get(mInitialState);//得到初始化的状态,按照如上的例子。此时是StateE。
            for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
                mTempStateStack[mTempStateStackCount] = curStateInfo;
                curStateInfo = curStateInfo.parentStateInfo;//根据父子关系,一各个元素初始化 mTempStateStack.
            }
            //从上面的for循环出来后m TempStateStack = [E,B,A]
            mStateStackTopIndex = -1;
            //接下来的方法是把 m TempStateStack = [E,B,A]。中的元素倒叙存入到 mStateStack 栈中。
            moveTempStateStackToStateStack();
            //调用完毕后, mStateStack  = [A,B,E]
    }

 SmHandler.moveTempStateStackToStateStack
                    private final int moveTempStateStackToStateStack() {//巧妙的把元素反转过来放入 mStateStack
                        int startingIndex = mStateStackTopIndex + 1;
            int i = mTempStateStackCount - 1;
            int j = startingIndex;
            while (i >= 0) {
                mStateStack[j] = mTempStateStack[i];
                j += 1;
                i -= 1;
            }
            mStateStackTopIndex = j - 1; // mStateStackTopIndex  这个最终是深度-1.
     
            return startingIndex;  //返回进入 mStateStack的索引,此时是0.代表StateA。
                  }
                  接下来SmHandler往外发送SM_INIT_CMD的消息,由它自身的handleMessage来处理。
        public final void handleMessage(Message msg) {
            if (!mHasQuit) {//这个标志位初始化默认事false,只有进入QuittingState以后才会变成true。
                mMsg = msg;
                State msgProcessedState = null;
                if (mIsConstructionCompleted) {
                   //初始化完毕后正常的消息处理会走到这里。但是此时 mIsConstructionCompleted默认事false,还没变成true
                    msgProcessedState = processMsg(msg);
                } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
                    //走到这里先把初始化的标志位 mIsConstructionCompleted置为true。
                    mIsConstructionCompleted = true;
                    invokeEnterMethods(0);//调用这个方法根据传输的 来决定调用那些State的enter方法。
                } else {
                    throw new RuntimeException("StateMachine.handleMessage: " + "The start method not called, received msg: " + msg);
                }
                    performTransitions(msgProcessedState, msg);//很重要的方法,每次消息处理都会调用。
            }
        }

        private final void invokeEnterMethods(int stateStackEnteringIndex) {
            for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {// mStateStackTopIndex进过前面的初始化栈已变成2
                mStateStack[i].state.enter();
                mStateStack[i].active = true;
            }
            //那么到此为止就是依次调用StateA 、B、E的enter的方法。并把它们对应的StateInfo的active的成员属性置为true。表示处于
            //激活状态。
        }

        private void performTransitions(State msgProcessedState, Message msg) {
            ......// 根据一些判断,把已处理完的消息放入到 mLogRecords当中 。(已走到handlemessage的最下面了。)
            State destState = mDestState;//根据 mDestState进行赋值。默认是null的。除非调用transitionTo(IState dest)它来进行设置。
            if (destState != null) {//此时的初始化是不会进入到这里的。
                while (true) {
                    StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                    invokeExitMethods(commonStateInfo);
                    int stateStackEnteringIndex = moveTempStateStackToStateStack();
                    invokeEnterMethods(stateStackEnteringIndex);
                    moveDeferredMessageAtFrontOfQueue();

                    if (destState != mDestState) {
                        destState = mDestState;
                    } else {
                        break;
                    }
                }
                mDestState = null;
            }

            if (destState != null) {  //看看是不是进入内部的退出和暂停状态,里面也没做什么特别的处理,可能是留着以后扩展。
                if (destState == mQuittingState) {
                    mSm.onQuitting();
                    cleanupAfterQuitting();
                } else if (destState == mHaltingState) {
                    mSm.onHalting();
                }
            }
        }
    至此算是完成了状态机的启动过程。此时的状态机处于StateE状态。接下来我们向状态机发送个消息来事状态机进入F状态。

2.3、状态的切换。
                           
                                                 
          往外发送携带一定参数的消息。走到前面所说的SmHandler.  handleMessage中。此时会走到第一个判断当中调用
          msgProcessedState = processMsg(msg);
        private final State processMsg(Message msg) {
            //此处虽然名字是Top..,其实代表的是StateStatck[]数组栈中的 最后一个。此时就是StateE状态。
            //其实这个会一直保持是你的房前状态
            StateInfo curStateInfo = mStateStack[mStateStackTopIndex];                            
            if (isQuit(msg)) {
                transitionTo(mQuittingState);
            } else {
               //然后会走到这里此时就会调用相应的State重写的 processMessage方法,对消息进行处理。如果你在方法中返回false
              //那么此处就会认为侧消息没有得到成功处理,会进入wile循环,把这个消息交给父状态进行处理,依次类推。
              //假如最终父状态都没有处理好,那么就会调用 unhandledMessage方法。(这个也可以重写)
                while (!curStateInfo.state.processMessage(msg)) {
                    curStateInfo = curStateInfo.parentStateInfo;
                    if (curStateInfo == null) {
                        mSm.unhandledMessage(msg);
                        break;
                    }
                }
            }
            return (curStateInfo != null) ? curStateInfo.state : null;//返回成功处理这个消息的状态。
        }
此时进行 processMessage处理时,本地例子的实现是:
                       
在这个方法中会调用 transitionTo( mStateF )使状态机的状态发生变化。
     private final void transitionTo(IState destState) {
           //设置 mDestState 在handlemessage执行到 performTransitions时,判断它是不是null。以便决定下面要不要执
                        //执行enter/exit。
       mDestState = (State) destState;
}
                          接一开始的状态切换,进入判断调用完process后,返回成功处理这个消息的State。没被处理时返回null。此例返回的是StateE。
                  然后接着调用 performTransitions(msgProcessedState, msg);进行对连接状态链的栈的更新和看看是否需要调用那些enter、exit方法。
                          此时由于前面设置了 mDestState。那么在这个方法中。
                               State destState = mDestState;
            if (destState != null) { //调用 transitionTo以后它就不是null了,而是我们想要跳转的state。
                while (true) {
                    //更新 TempStateStack,返回当前状态和目标状态的最小公共父状态。
                    StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                    invokeExitMethods(commonStateInfo);//以公共父状态为为准在 TempStateStack中,前面的全调用exit,因为它是从下往上存
                    int stateStackEnteringIndex = moveTempStateStackToStateStack();//把 TempStateStack中的元素反转并保存在 StateStack
                    invokeEnterMethods(stateStackEnteringIndex);//以公共的父状态为准,栈里面后面的全部调用enter。因为它是从上往下存的。
                    //把所有前面通过deferMessage()发送过来的消息进行处理。(由此状态处理,和谁发送的这个deferMessage无关)
                    //处理时有好几个的时候哪找先后存入的顺序进行处理。
                    moveDeferredMessageAtFrontOfQueue();
                    if (destState != mDestState) {
                        destState = mDestState;//如果有新的 mDestState时,继续循环。
                    } else {
                        break;//如果没有设置新的,那么退出这个while循环。
                    }
                }
                mDestState = null;
            }
       //返回公共父类。
       //注意这个类有一个乍一看特殊其实也正常的情况。比如按照这个例子,从状态E--B的时候。
       //此时会返回共同的祖先A,接下来会先退出这B、E,然后进入B。乍一看不应该再退出B。其实也是按照正常逻辑走的。
       //此处的设计思想就是退出父类active的这个分支,然后在进入父类的需要进入的active==false的分支。只不过此时重复了一个B状态.
       //这是由于通过下面的放法 mTempStateStack=[B,B,A].然后就会调用B的退出方法。且count==1;只循环了一次。
        private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
            mTempStateStackCount = 0;
            StateInfo curStateInfo = mStateInfo.get(destState);
            //取出目标State,然后依次从目标State开始往上层state查找,并更新相应的 mTempStateStack位置的状态。
            //此时的判断条件是,要么当前状态是null或者当前状态时激活的也就是active==true,那么就不继续查找了。
            //注意此时只是更新相应的位置的变化,由于 mTempStateStack在前面已经付过值,本例
            // mTempStateStack = [E ,B ,A]     此时依次更改完毕后 [F,C,A]      mTempStateStackCount = 2;
            //此时只是循环了两边,count==2, mTempStateStack只更新了前两个元素,第三个本来就是A。
            do {
                mTempStateStack[mTempStateStackCount++] = curStateInfo;
                curStateInfo = curStateInfo.parentStateInfo;
            } while ((curStateInfo != null) && !curStateInfo.active);\\由于A.active==true,退出循环。 
                //返回这个公共的父类。
                 return curStateInfo;  
        }
private final void invokeExitMethods(StateInfo commonStateInfo) {
           //配合 mStateStack栈,由于他层次上来说是从上往下存储的,那么在公共父状态之前的都应噶调用退出。
            while ((mStateStackTopIndex >= 0)  && (mStateStack[mStateStackTopIndex] != commonStateInfo)) {//是否等于公共父状态
                State curState = mStateStack[mStateStackTopIndex].state;//最下层的这个状态开始,也就是栈中最下面的,
                curState.exit();
                mStateStack[mStateStackTopIndex].active = false;
                mStateStackTopIndex -= 1;   //一次减1,知道等于公共改的父状态。一定要注意这个是成员变量会变的。
            }
}

剩下的moveTempStateStackToStateStack、invokeEnterMethods已经在前面分析过。此处也同理。google实现的却很巧妙。至此一个状态切换的主要流程处理完毕。
3、流程概述。
          总体来说,先写一个类继承StateMachine。然后调用addstate添加各个状态的树形结构。然后调用 setInitialState来设置初
始化状态,最后调用start()来启动状态机,此时会计算最大深度去初始化 mStateStack、mTempStateStack两个栈。然后
调用相应状态的enter方法进入到当前状态。往外发送SM_INIT_CMD消息。在ihandleMessage中进行处理。
    然后在消息的驱动下。首先会调用到SmHandler的handleMessage方法,然后会调用processMsg,最终这个方法中会
调用重写的processMessage()方法让当前状态或者父状态处理。最后处理完消息后会调用performTransitions来看是否
需要更新 mStateStack、mTempStateStack两个栈,以及是否需要调用enter、exit。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值