从AOSP源码WifiStateMachine入手分析Android状态机设计思想

最近独立开发了项目的一个部分,关于设计模式方面总是经过很多改动也没有得到最理想的结构,刚好借着这个机会详细了解一下Android的状态机设计思想,选取的demo代码就是WifiStateMachine,为了能详细的剖析该设计思想,将WiFi处理逻辑相关的代码尽可能的忽略,只抓住该类的设计模式进行分析。

首先是状态的构建:

        // CHECKSTYLE:OFF IndentationCheck
        addState(mDefaultState);
            addState(mInitialState, mDefaultState);
            addState(mSupplicantStartingState, mDefaultState);
            addState(mSupplicantStartedState, mDefaultState);
                    addState(mScanModeState, mSupplicantStartedState);
                    addState(mConnectModeState, mSupplicantStartedState);
                        addState(mL2ConnectedState, mConnectModeState);
                            addState(mObtainingIpState, mL2ConnectedState);
                            addState(mConnectedState, mL2ConnectedState);
                            addState(mRoamingState, mL2ConnectedState);
                        addState(mDisconnectingState, mConnectModeState);
                        addState(mDisconnectedState, mConnectModeState);
                        addState(mWpsRunningState, mConnectModeState);
                addState(mWaitForP2pDisableState, mSupplicantStartedState);
            addState(mSupplicantStoppingState, mDefaultState);
            addState(mSoftApState, mDefaultState);
        // CHECKSTYLE:ON IndentationCheck

可以看到WiFi模块的状态比较复杂,可以用一个树的形式来表示,概念图如下:
在这里插入图片描述
添加状态机的函数的实现在父类StateMachine中,如下:


        /**
         * Add a new state to the state machine. Bottom up addition
         * of states is allowed but the same state may only exist
         * in one hierarchy.
         *
         * @param state the state to add
         * @param parent the parent of state
         * @return stateInfo for this state
         */
        private final StateInfo addState(State state, State parent) {
            if (mDbg) {
                mSm.log("addStateInternal: E state=" + state.getName() + ",parent="
                        + ((parent == null) ? "" : parent.getName()));
            }
            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);
            }

            // Validate that we aren't adding the same state in two different hierarchies.
            if ((stateInfo.parentStateInfo != null)
                    && (stateInfo.parentStateInfo != parentStateInfo)) {
                throw new RuntimeException("state already added");
            }
            stateInfo.state = state;
            stateInfo.parentStateInfo = parentStateInfo;
            stateInfo.active = false;
            if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
            return stateInfo;
        }

经过一些初始化如设置初始状态后会调用start来启动状态机

    /**
     * Start the state machine.
     */
    public void start() {
        // mSmHandler can be null if the state machine has quit.
        SmHandler smh = mSmHandler;
        if (smh == null) return;

        /** Send the complete construction message */
        smh.completeConstruction();
    }

        /**
         * Complete the construction of the state machine.
         */
        private final void completeConstruction() {
            if (mDbg) mSm.log("completeConstruction: E");

            /**
             * Determine the maximum depth of the state hierarchy
             * so we can allocate the state stacks.
             */
            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) mSm.log("completeConstruction: maxDepth=" + maxDepth);

            mStateStack = new StateInfo[maxDepth];
            mTempStateStack = new StateInfo[maxDepth];
            setupInitialStateStack();

            /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
            sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));

            if (mDbg) mSm.log("completeConstruction: X");
        }

该方法计算出了状态机层次结构的深度,同时维护了两个状态数组mStateStack 和mTempStateStack ,并将初始状态开始直至根节点的状态存储到mTempStateStack中,将该结果再逆序存储到mStateStack 中。
之后调用sendMessageAtFrontOfQueue方法,通过mSmHandler将SM_INIT_CMD指令发送到StateMachine的MessageQueue中,其中mSmHandler的构造是在StateMachine的构造中完成的:

protected StateMachine(String name) {
        mSmThread = new HandlerThread(name);
        mSmThread.start();
        Looper looper = mSmThread.getLooper();

        mName = name;
        mSmHandler = new SmHandler(looper, this);
    }

接下来就是状态机对指令的核心处理逻辑handleMessage:


        /**
         * Handle messages sent to the state machine by calling
         * the current state's processMessage. It also handles
         * the enter/exit calls and placing any deferred messages
         * back onto the queue when transitioning to a new state.
         */
        @Override
        public final void handleMessage(Message msg) {
            if (!mHasQuit) {
                if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
                    mSm.onPreHandleMessage(msg);
                }

                if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);

                /** Save the current message */
                mMsg = msg;

                /** State that processed the message */
                State msgProcessedState = null;
                if (mIsConstructionCompleted) {
                    /** Normal path */
                    // processMsg会依次调用各个状态内部的processMessage
                    msgProcessedState = processMsg(msg);
                } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
                        && (mMsg.obj == mSmHandlerObj)) {
                    /** Initial one time path. */
                    mIsConstructionCompleted = true;
                    invokeEnterMethods(0);
                } else {
                    throw new RuntimeException("StateMachine.handleMessage: "
                            + "The start method not called, received msg: " + msg);
                }
                // 经过上一步的处理之后mDestState已经被改变,需要整理状态树
                performTransitions(msgProcessedState, msg);

                // We need to check if mSm == null here as we could be quitting.
                if (mDbg && mSm != null) mSm.log("handleMessage: X");

                if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
                    mSm.onPostHandleMessage(msg);
                }
            }
        }

processMsg的实现如下:

        /**
         * Process the message. If the current state doesn't handle
         * it, call the states parent and so on. If it is never handled then
         * call the state machines unhandledMessage method.
         * @return the state that processed the message
         */
        private final State processMsg(Message msg) {
            StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
            if (mDbg) {
                mSm.log("processMsg: " + curStateInfo.state.getName());
            }

            if (isQuit(msg)) {
				// 如果msg.what == SM_QUIT_CMD直接切换到退出状态
                transitionTo(mQuittingState);
            } else {
				/** 
				 * 从状态树的最小子节点开始寻找能够processMessage的状态,
				 * 如果找不到就向上移到他的父节点看是否能处理
				 * 在某个状态处理了processMessage之后,会改变mDestState为下一个状态
				 */
                while (!curStateInfo.state.processMessage(msg)) {
                    /**
                     * Not processed
                     */
                    curStateInfo = curStateInfo.parentStateInfo;
                    if (curStateInfo == null) {
                        /**
                         * No parents left so it's not handled
                         */
                        mSm.unhandledMessage(msg);
                        break;
                    }
                    if (mDbg) {
                        mSm.log("processMsg: " + curStateInfo.state.getName());
                    }
                }
            }
			// 返回处理了这个事件的状态
            return (curStateInfo != null) ? curStateInfo.state : null;
        }

由于第一次调用handleMessage函数的时候未进行初始化,因此会调用invokeEnterMethods来将状态进行初始化:

                mStateStack[i].state.enter();
                mStateStack[i].active = true;

在执行完初始化逻辑后会调用performTransitions函数,这个函数的实现比较复杂,目的是完成下一个目的状态对应的状态树的enter调用和已完成状态对应的状态树中无用状态的exit调用。由于初始化环节destState == null,所以不会走到复杂逻辑中,但是为了之后的分析,我们将这个函数梳理一遍。


        /**
         * Do any transitions
         * @param msgProcessedState is the state that processed the message
         */
        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;

            /**
             * Record whether message needs to be logged before we transition and
             * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which
             * always set msg.obj to the handler.
             */
            boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);

            if (mLogRecords.logOnlyTransitions()) {
                /** Record only if there is a transition */
                if (mDestState != null) {
                    mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
                            orgState, mDestState);
                }
            } else if (recordLogMsg) {
                /** Record message */
                mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,
                        mDestState);
            }

            State destState = mDestState;
            if (destState != null) {
                /**
                 * Process the transitions including transitions in the enter/exit methods
                 */
                while (true) {
                    if (mDbg) mSm.log("handleMessage: new destination call exit/enter");

                    /**
                     * 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.
                     */
                    StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                    invokeExitMethods(commonStateInfo);
                    // 将临时堆栈中的内容复制到状态树中,并且将终点记录为mStateStackTopIndex,将起点返回
                    int stateStackEnteringIndex = moveTempStateStackToStateStack();
                    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.
                     */
                    moveDeferredMessageAtFrontOfQueue();

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

            /**
             * After processing all transitions check and
             * see if the last transition was to quit or halt.
             */
            if (destState != null) {
                if (destState == mQuittingState) {
                    /**
                     * Call onQuitting to let subclasses cleanup.
                     */
                    mSm.onQuitting();
                    cleanupAfterQuitting();
                } else if (destState == mHaltingState) {
                    /**
                     * Call onHalting() if we've transitioned to the halting
                     * state. All subsequent messages will be processed in
                     * in the halting state which invokes haltedProcessMessage(msg);
                     */
                    mSm.onHalting();
                }
            }
        }

setupTempStateStackWithStatesToEnter的实现如下:

        private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
            /**
             * Search up the parent list of the destination state for an active
             * state. Use a do while() loop as the destState must always be entered
             * even if it is active. This can happen if we are exiting/entering
             * the current state.
             */
            mTempStateStackCount = 0;
            // curStateInfo 为要切换的目的状态
            StateInfo curStateInfo = mStateInfo.get(destState);
            do {
                // 以目的状态为起点,循环记录目的状态树中未激活的状态,并将这些状态依次压入mTempStateStack中
                mTempStateStack[mTempStateStackCount++] = curStateInfo;
                curStateInfo = curStateInfo.parentStateInfo;
            } while ((curStateInfo != null) && !curStateInfo.active);

            if (mDbg) {
                mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
                        + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
            }
            // 返回的状态是已经被激活的,其实是旧状态树与新状态树的交点
            return curStateInfo;
        }

invokeExitMethods的实现如下,目的是将旧状态树中不再使用的状态退出,调用exit方法,active 置为false,通过setupTempStateStackWithStatesToEnter函数已经找出了新老状态树的交点,也就是清理的终点:

        /**
         * Call the exit method for each state from the top of stack
         * up to the common ancestor state.
         */
        private final void invokeExitMethods(StateInfo commonStateInfo) {
            while ((mStateStackTopIndex >= 0)
                    && (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
                State curState = mStateStack[mStateStackTopIndex].state;
                if (mDbg) mSm.log("invokeExitMethods: " + curState.getName());
                curState.exit();
                // 将标志位置为false,等待后续回收清理
                mStateStack[mStateStackTopIndex].active = false;
                mStateStackTopIndex -= 1;
            }
        }

之后调用moveTempStateStackToStateStack()和invokeEnterMethods(stateStackEnteringIndex),代码实现相同,这里不再进行赘述,至此旧状态对指令的处理,状态的切换、旧状态的清理和新状态的enter已全部完成。
接下来选取状态机真正执行流程中的一部分来分析setSupplicantRunning函数:

        if (enable) {
            sendMessage(CMD_START_SUPPLICANT);
        } else {
            sendMessage(CMD_STOP_SUPPLICANT);
        }

这个指令会由InitialState来处理:

case CMD_START_SUPPLICANT:
                    mClientInterface = mWifiNative.setupForClientMode();
                    if (mClientInterface == null
                            || !mDeathRecipient.linkToDeath(mClientInterface.asBinder())) {
                        setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
                        cleanup();
                        break;
                    }

                    try {
                        // A runtime crash or shutting down AP mode can leave
                        // IP addresses configured, and this affects
                        // connectivity when supplicant starts up.
                        // Ensure we have no IP addresses before a supplicant start.
                        mNwService.clearInterfaceAddresses(mInterfaceName);

                        // Set privacy extensions
                        mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);

                        // IPv6 is enabled only as long as access point is connected since:
                        // - IPv6 addresses and routes stick around after disconnection
                        // - kernel is unaware when connected and fails to start IPv6 negotiation
                        // - kernel can start autoconfiguration when 802.1x is not complete
                        mNwService.disableIpv6(mInterfaceName);
                    } catch (RemoteException re) {
                        loge("Unable to change interface settings: " + re);
                    } catch (IllegalStateException ie) {
                        loge("Unable to change interface settings: " + ie);
                    }

                    if (!mWifiNative.enableSupplicant()) {
                        loge("Failed to start supplicant!");
                        setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
                        cleanup();
                        break;
                    }
                    if (mVerboseLoggingEnabled) log("Supplicant start successful");
                    mWifiMonitor.startMonitoring(mInterfaceName, true);
                    setSupplicantLogLevel();
                    // 将mDestState设置为mSupplicantStartingState
                    transitionTo(mSupplicantStartingState);
                    break;

调用native函数进行核心的处理逻辑后会调用transitionTo将状态切换到SupplicantStartingState,具体的切换逻辑已在上文分析过,setSupplicantRunning函数通过WifiStateMachine执行CMD_START_SUPPLICANT逻辑并切换状态等待下一步的指令。

分析到这项目中的实现已经有了大概的想法,最后总结一下,Android已经给出了很多现成的轮子,但是很多时候照搬轮子会造成技术浪费,统观WifiStateMachine,具有复杂的状态切换逻辑,原因是WiFi多种的状态,而自己要实现的功能是否需要如此复杂还需要进一步研讨。

目前刚完成了一个核心就是状态机思想的项目,但相比于Android的WifiStateMachine还是小巫见大巫,主要区别一是使用的语言不同,二是我的状态机只有当前状态能处理事件,而WifiStateMachine是从整个状态树中寻找可以处理事件的状态,找到后的状态切换也不是简单的单状态更改,而是更改整个状态树。

最后附上学习状态机时写的demo代码,可以方便与大家理解:
https://github.com/woyelaishishiba/StateMachineDemo

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值