StateMachine

84 阅读8分钟

State设计模式

将对象的状态封装成1个对象,在不同状态下,同样的调用将执行不同的操作

State设计模式类图

Context可维护多个State子类的实例,每个实例代表1种状态。State定义1个接口,以封装与Context的1个特定状态相关的行为,即Context只需固定调用State→Handle方法,即可将与状态相关的请求委托给当前ConcreteState对象的Handle方法处理。

状态机的四大概念

State

1个状态机至少要包含2个状态。

Event

事件就是执行某个操作的触发条件或口令。

Action

事件发生以后要执行动作。

Transition

从1个状态变化为另1个状态。

状态机使用步骤

1. 定义1个类,继承StateMachine

public class GwsdModeController extends StateMachine {

2. 定义各个状态,并且重写enter(),exit(),processMessage()

enter()是每次进入都会执行类似构造函数

exit()类似析构函数

processMessage()用来处理不同的请求

    private class InacviceState extends State {
        @Override
        public void enter() {
            mState = "InacviceState";
            logd("enter");
        }
        @Override
        public void exit() {
            logd("exit");
        }
        @Override
        public boolean processMessage(Message msg) {
            boolean retVal = HANDLED;
            logd("msg=" + msgToString(msg));
            AsyncResult ar = null;
            Message callback = null;
            if (msg.obj instanceof AsyncResult) {
                ar = (AsyncResult) msg.obj;
                callback = (Message) ar.userObj;
            } else if (msg.obj instanceof Message) {
                callback = (Message) msg.obj;
            }
            switch(msg.what) {
                case EVENT_SET_NORMAL_MODE:
                    if (msg.arg1 == 0) {
                        AsyncResult.forMessage(callback, null, null);
                        callback.sendToTarget();
                        return retVal;
                    }
                    MtkRIL mtkRil = getRilService(msg, false);
                    if (mtkRil != null) {
                        msg = obtainMessage(EVENT_SET_NORMAL_MODE_COMPLETED,
                            msg.arg1, -1, msg.obj);
                        mtkRil.setGwsdMode(msg.arg1, KEEP_BY_IP, KEEP_START, msg);
                        transitionTo(mActivatingState);
                    }
                    break;
                default:
                    logd("no action");
                    retVal = NOT_HANDLED;
                    break;
            }
            return retVal;
        }
    }

3. 调用addState构建状态树

状态机从添加状态开始,调用 addState 方法。每个State都可添加1个自身的State和1个parentState。若在当前的State中没能处理发送来的msg时,就要向上找其父State来处理,依次向上遍历,若所有的State都不能处理就会调用SmHandler:unhandledMessage来处理。

    public GwsdModeController(String name, int phoneId,
            Handler handler, Context context) {
        super(name, handler);
。。。。。。
        addState(mDefaultState);
        addState(mInacviceState, mDefaultState);
        addState(mActivatingState, mDefaultState);
        addState(mActiveState, mDefaultState);
        addState(mDeactivatingState, mDefaultState);
。。。。。。
    }

几种状态的依赖关系如下:

4. setInitialState()设置初始状态

    public GwsdModeController(String name, int phoneId,
            Handler handler, Context context) {
        super(name, handler);
。。。。。。
        //mInacviceState为初始状态
        setInitialState(mInacviceState);
。。。。。。
    }

5. 调用start(),状态机开始工作

    public GwsdModeController(String name, int phoneId,
            Handler handler, Context context) {
。。。。。。
        start();
    }

6. 使用sendMessage()向状态机中发送消息

sendMessage发送消息时,StateMachine就会找到当前的State调用其processMessage来做相应的处理动作

    public void setUserSelectionMode(boolean action, Message onComplete) {
        logd("setUserSelectionMode: action = " + action);
        synchronized(mLock) {
            int mode = action ? 1 : 0;
            Message msg = obtainMessage(EVENT_SET_NORMAL_MODE, mode, -1);
            msg.obj = onComplete;
            sendMessage(msg);
        }
    }

7. transitionTo()从当前转移到另外1个状态

transitionTo(mActivatingState);

StateMachine核心类

SmHandler

在 StateMachine中,开启了1个线程HandlerThread,对应的Handler为SmHandler。因此上文案例中对应状态的 processMessage(Message msg),均在HandlerThread线程中执行。

是消息处理派发和状态控制切换的核心,运行在单独的线程上

StateMachine类对外提供状态相关操作的接口方法,而SmHandler类则是作为StateMachine状态机的核心,负责Handler消息的发送和接收,用来管理和更新State对象。

StateMachine的构造方法

StateMachine的构造方法中,创建并启动了1个线程HandlerThread;

protected StateMachine(String name) {
    // 创建 HandlerThread
    mSmThread = new HandlerThread(name);
    mSmThread.start();
    // 获取HandlerThread对应的Looper
    Looper looper = mSmThread.getLooper();
    // 初始化 StateMachine
    initStateMachine(name, looper);
}

initStateMachine方法中,创建了HandlerThread线程对应的Handler SmHandler

private void initStateMachine(String name, Looper looper) {
    mName = name;
    mSmHandler = new SmHandler(looper, this);
}

SmHandler构造方法中,向状态机中添加了2个状态:1个状态为状态机的暂停状态mHaltingState、1个状态为状态机的退出状态mQuittingState

private SmHandler(Looper looper, StateMachine sm) {
    super(looper);
    mSm = sm;


    // 添加状态:暂停 和 退出
    // 这2个状态 无父状态
    addState(mHaltingState, null);
    addState(mQuittingState, null);
}

mHaltingState状态,顾名思义让状态机暂停,其对应的processMessage(Message msg),返回值为true,将消息消费掉,但不处理消息。从而使状态机状态停顿到mHaltingState状态

mQuittingState状态,若进入该状态, 状态机将退出。HandlerThread线程对应的Looper将退出,HandlerThread线程会被销毁,所有加入到状态机的状态被清空。

StateMachine的start方法

状态机的初始化说完,下边来说状态机的启动方法start()

public void start() {
    // mSmHandler can be null if the state machine has quit.
    SmHandler smh = mSmHandler;
    // StateMachine 未进行初始化,为什么不抛出1个异常
    if (smh == null) {
        return;
    }
    // 完成状态机建设
   smh.completeConstruction();
}

从以上代码可看到,其中只有1个方法completeConstruction(),用于完成状态机的建设。

private final void completeConstruction() {
    int maxDepth = 0;
    // 循环判断所有状态,看看哪1个链最长,得出深度
    for (StateInfo si : mStateInfoHashMap.values()) {
        int depth = 0;
        for (StateInfo i = si; i != null; depth++) {
            i = i.parentStateInfo;
        }
        if (maxDepth < depth) {
            maxDepth = depth;
        }
    }
    // 状态堆栈
    mStateStack = new StateInfo[maxDepth];
    // 临时状态堆栈
    mTempStateStack = new StateInfo[maxDepth];
    // 初始化堆栈
    setupInitialStateStack();


    // 发送初始化完成的消息(消息放入到队列的最前边)
    sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
}
  • maxDepth是状态机中,最长依赖链的长度。
  • mStateStack与mTempStateStack为2个用数组实现的堆栈。这2个堆栈的最大长度,即为maxDepth。其用来存储当前活跃状态与当前活跃状态的父状态、父父状态、...等
  • setupInitialStateStack();完成状态的初始化,将当前的活跃状态放入到mStateStack堆栈中。

下边来具体说setupInitialStateStack()中,如何完成栈的初始化。

private final void setupInitialStateStack() {
    // 获取初始状态信息
    StateInfo curStateInfo = mStateInfoHashMap.get(mInitialState);
    //
    for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
        // 初始状态 放入临时堆栈
        mTempStateStack[mTempStateStackCount] = curStateInfo;
        // 当前状态的 所有父状态 1级级放入堆栈
        curStateInfo = curStateInfo.parentStateInfo;
    }


    // 清空 状态堆栈
    // Empty the StateStack
    mStateStackTopIndex = -1;
    // 临时堆栈 换到 状态堆栈
    moveTempStateStackToStateStack();
}
  • 将初始化状态放入 mTempStateStack堆栈中
  • 将初始化状态的父状态、父父状态、父父父状态... 都11放入到mTempStateStack堆栈中

然后moveTempStateStackToStateStack()中,mTempStateStack出栈,mStateStack入栈,将所有状态信息导入到mStateStack堆栈,并清空mTempStateStack堆栈。

到这里,初始化基本完成,但还落下1部分代码没说:

// 发送初始化完成的消息(消息放入到队列的最前边)
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
  • 发送1个初始化完成的消息到SmHandler当中。

下边来看下SmHandler的handleMessage(Message msg)方法:

public final void handleMessage(Message msg) {


    // 处理消息
    if (!mHasQuit) {
        // 保存传入的消息
        mMsg = msg;
        State msgProcessedState = null;
        // 已完成初始化
        if (mIsConstructionCompleted) {
		// ..
        }
        // 接收到 初始化完成的消息
        else if (!mIsConstructionCompleted
                && (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
            /** Initial one time path. */
            // 初始化完成
            mIsConstructionCompleted = true;
            // 调用堆栈中状态的enter方法,并将堆栈中的状态设置为活跃状态
            invokeEnterMethods(0);
        } else {
		// ..
        }
        // 执行Transition
        performTransitions(msgProcessedState, msg);
    }
}
  • 接收到初始化完成的消息后mIsConstructionCompleted = true;对应的标志位变过来
  • 执行 invokeEnterMethods方法将mStateStack堆栈中的所有状态设置为活跃状态,并由父—>子的顺序,执行堆栈中状态的enter()
  • performTransitions(msgProcessedState, msg);在start()时,其中的内容全部不执行,因此先不介绍。

invokeEnterMethods方法的方法体如下:

        private final void invokeEnterMethods(int stateStackEnteringIndex) {
            for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
                if (stateStackEnteringIndex == mStateStackTopIndex) {
                    // Last enter state for transition
                    mTransitionInProgress = false;
                }
                if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
                mStateStack[i].state.enter();
                mStateStack[i].active = true;
            }
            mTransitionInProgress = false; // ensure flag set to false if no methods called
        }
  • 可看到,其将mStateStack堆栈中的所有状态设置为活跃状态,并由父—>子的顺序,执行堆栈中状态的enter()

到此start()完成,最终mStateStack堆栈状态,也如上图所示。

状态转化

  • 通过调用sendMessage方法,向SmHandler中发送1个消息,来触发状态转化。
  • 可说 sendMessage为状态转化的导火索。

下边,再次看下SmHandler的handleMessage(Message msg):

public final void handleMessage(Message msg) {
    // 处理消息
    if (!mHasQuit) {
        // 保存传入的消息
        mMsg = msg;
        State msgProcessedState = null;
        // 已完成初始化
        if (mIsConstructionCompleted) {
            // 处理消息的状态
            msgProcessedState = processMsg(msg);
        }
        // 收到 初始化完成的消息
        else if (!mIsConstructionCompleted
                && (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
            // 初始化完成
            mIsConstructionCompleted = true;
            // 调用堆栈中状态的enter方法,并将堆栈中的状态设置为活跃状态
            invokeEnterMethods(0);
        } else {
            throw new RuntimeException("StateMachine.handleMessage: "
                    + "The start method not called, received msg: " + msg);
        }
        // 执行Transition
        performTransitions(msgProcessedState, msg);
    }
}

  • 因为初始化已经完成,代码会直接走到processMsg(msg);中。

来看processMsg(msg);:

private final State processMsg(Message msg) {
    // 堆栈中找到当前状态
    StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
    // 是否为退出消息
    if (isQuit(msg)) {
        // 转化为退出状态
        transitionTo(mQuittingState);
    } else {
        // 状态返回true 则是可处理此状态
        // 状态返回false 则不可处理
        while (!curStateInfo.state.processMessage(msg)) {
            // 当前状态的父状态
            curStateInfo = curStateInfo.parentStateInfo;
            // 父状态未null
            if (curStateInfo == null) {
                // 回调到未处理消息方法中
                mSm.unhandledMessage(msg);
                break;
            }
        }
    }
    // 消息处理后,返回当前状态信息
    // 若消息不处理,则返回其父状态处理,返回处理消息的父状态
    return (curStateInfo != null) ? curStateInfo.state : null;
}
  • 代码会直接走到while (!curStateInfo.state.processMessage(msg))执行mStateStack堆栈中,最上层状态的 processMessage(msg)。
  • 这里若mStateStack堆栈中状态的processMessage(msg)返回true,则表示其消费掉了此消息;若其返回false,则表示不消费此消息,那么该消息将继续向其父状态进行传递;
  • 最终将返回,消费掉该消息的状态。

这里,堆栈对上层的状态为InacviceState。所以看下其对应的processMessage(msg)。

    private class 在SleepState状态的processMessage(Message msg)方法中,其收到MSG_WAKEUP消息后,会调用transitionTo(mWorkState);方法,将目标状态设置为mWorkState。 extends State {
        @Override
        public void enter() {
            mState = "InacviceState";
            logd("enter");
        }
        @Override
        public void exit() {
            logd("exit");
        }
        @Override
        public boolean processMessage(Message msg) {
            boolean retVal = HANDLED;
            logd("msg=" + msgToString(msg));
            AsyncResult ar = null;
            Message callback = null;
            if (msg.obj instanceof AsyncResult) {
                ar = (AsyncResult) msg.obj;
                callback = (Message) ar.userObj;
            } else if (msg.obj instanceof Message) {
                callback = (Message) msg.obj;
            }
            switch(msg.what) {
                case EVENT_SET_NORMAL_MODE:
                    if (msg.arg1 == 0) {
                        AsyncResult.forMessage(callback, null, null);
                        callback.sendToTarget();
                        return retVal;
                    }
                    MtkRIL mtkRil = getRilService(msg, false);
                    if (mtkRil != null) {
                        msg = obtainMessage(EVENT_SET_NORMAL_MODE_COMPLETED,
                            msg.arg1, -1, msg.obj);
                        mtkRil.setGwsdMode(msg.arg1, KEEP_BY_IP, KEEP_START, msg);
                        transitionTo(mActivatingState);
                    }
                    break;
                default:
                    logd("no action");
                    retVal = NOT_HANDLED;
                    break;
            }
            return retVal;
        }
    }
  • 在SleepState状态的processMessage(Message msg)中,其收到EVENT_SET_NORMAL_MODE消息后,会调用transitionTo(mActivatingState);,将目标状态设置为mActivatingState。

看下transitionTo(mWorkState);:

private final void transitionTo(IState destState) {
    mDestState = (State) destState;
}
  • 可看到,transitionTo(IState destState),只是1个简单的状态赋值。

下边回到SmHandler的handleMessage(Message msg):

  • 会执行到SmHandler.handleMessage(Message msg)的performTransitions(msgProcessedState, msg)之中。

  • 而这里传入的参数msgProcessedState为mActivatingState。

    private void performTransitions(State msgProcessedState, Message msg) { // 当前状态 State orgState = mStateStack[mStateStackTopIndex].state; // ... // 目标状态 State destState = mDestState; if (destState != null) { while (true) { // 目标状态 放入temp 堆栈 // 目标状态的 父状态 作为参数 传入下1级 StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState); // commonStateInfo 状态的子状态全部退栈 invokeExitMethods(commonStateInfo); // 目标状态入栈 int stateStackEnteringIndex = moveTempStateStackToStateStack(); // 入栈状态 活跃 invokeEnterMethods(stateStackEnteringIndex); //... moveDeferredMessageAtFrontOfQueue();

            if (destState != mDestState) {
                // A new mDestState so continue looping
                destState = mDestState;
            } else {
                // No change in mDestState so we're done
                break;
            }
        }
        mDestState = null;
    }
    // ...
    

    }

  • 以上方法中 传入的参数msgProcessedState为InacviceState

  • 方法中destState目标状态为 mActivatingState

此时performTransitions(State msgProcessedState, Message msg)中内容的执行示意图:

A、目标状态放入到mTempStateStack队列中

// 目标状态 放入temp 堆栈
// 目标状态的 父状态 作为参数 传入下1级
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
  • 1、将mActivatingState状态放入到mTempStateStack堆栈中
  • 2、将mActivatingState状态的非活跃父状态mDefaultState11入mTempStateStack堆栈

B、commonStateInfo状态在mStateStack堆栈中的子状态退堆栈

commonStateInfo为setupTempStateStackWithStatesToEnter(destState);方法的返回参数。这里是mDefaultState

// commonStateInfo 状态的子状态全部退栈
invokeExitMethods(commonStateInfo);
  • 1、mDefaultState作为参数传入到invokeExitMethods(commonStateInfo);方法中
  • 2、其方法内容为,将mDefaultState状态的全部子状态退堆栈

以上代码的执行示意图:

C、mTempStateStack全部状态出堆栈,mStateStack入堆栈

// 目标状态入栈
int stateStackEnteringIndex = moveTempStateStackToStateStack();
// 入栈状态 活跃
invokeEnterMethods(stateStackEnteringIndex);
  • moveTempStateStackToStateStack中:mTempStateStack全部状态出堆栈,mStateStack入堆栈
  • invokeEnterMethods(stateStackEnteringIndex);中,将新加入的状态设置为活跃状态;并调用其对应的enter()。

最终的堆栈状态为:

参考:

blog.51cto.com/u_15127582/…