【安卓Framework学习】Wifi框架学习之wifi状态机

系列文章目录

【安卓Framework学习】Wifi框架学习之核心类.
【安卓Framework学习】Wifi框架学习之开启与关闭流程.
【安卓Framework学习】Wifi框架学习之连接与断开流程.
【安卓Framework学习】Wifi框架学习之扫描模式及扫描流程.
【安卓Framework学习】Wifi框架学习之热点评分机制.
【安卓Framework学习】安卓连接管理(ConnectivityService)之wifi连接及注册.


文章目录

  • 系列文章目录
  • 前言
  • 一、设计模式之状态模式
    • 1.状态模式介绍
    • 2.状态模式主要类
  • 二、wifi框架中的状态机
    • 1.wifi框架中状态机及常用方法
      • 1.1状态机的初始化和启动
      • 1.2状态机的消息处理及状态切换
      • 1.3状态机的退出
    • 2.wifi框架中出现的状态机
      • 2.1 ActiveModeWarden.WifiController
      • 2.2 ClientModeManager.ClientModeStateMachine
      • 2.3 ClientModeImpl
  • 总结


前言

上一篇重点介绍了wifi的开关流程【安卓Framework学习】Wifi框架学习之开启与关闭流程,由于中间涉及到较多的状态机,本篇想对wifi框架中的各类状态机详细介绍一下。本篇中涉及到的安卓源码均来源于安卓11.


一、设计模式之状态模式

1.状态模式介绍

状态模式是设计模式中的行为模式的一种,对于其解释网络上解释的相当全面了。通俗一点来说,就是某个对象,包含一些动作或方法,但是这些动作和方法根据对象目前所处状态不同会有不同的运行方式或者会产生不同的结果。就类比手机会有不同的运行状态,例如关机、开机、息屏、休眠、有网络、没网络等,但是在这些状态中可能都会执行某个方法,一旦状态多了后,这个方法中就会出现非常多的条件判断(switch或者if...else),非常不利于程序的扩展。如果此时利用状态模式来写,就能够将其中较多的分支化解开,将繁杂的条件判断分支,通过不同的类来表达,对象或设备处于哪个状态,就由哪个状态中的方法来执行,虽然类变多了,但是代码变得清晰了,并且直接消除了繁多的条件判断语句。

以下状态模式优缺点来自于link:
优点:
1、封装了转换规则。
2、枚举可能的状态,在枚举状态之前需要确定状态种类。
3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点:
1、状态模式的使用必然会增加系统类和对象的个数。
2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

2.状态模式主要类

状态模式的具体实现,首先需要一个状态的抽象类,去定义所有状态的公共方法,一下源码来自安卓11,如下。

public class State implements IState {
    protected State() {
    }

    public void enter() {
    }

    public void exit() {
    }

    public boolean processMessage(Message msg) {
        return false;
    }

    /**
     * Name of State for debugging purposes.
     *
     * This default implementation returns the class name, returning
     * the instance name would better in cases where a State class
     * is used for multiple states. But normally there is one class per
     * state and the class name is sufficient and easy to get. You may
     * want to provide a setName or some other mechanism for setting
     * another name if the class name is not appropriate.
     *
     * @see com.android.internal.util.IState#processMessage(android.os.Message)
     */
    public String getName() {
        String name = getClass().getName();
        int lastDollar = name.lastIndexOf('$');
        return name.substring(lastDollar + 1);
    }
}

安卓中定义的状态类主要是有enter()exit()processMessage()三个方法,分别表示:

  • enter() 进入状态时需要执行的方法,主要做一些进入状态的准备工作
  • exit() 退出状态时需要执行的方法,主要做一些退出状态的收尾工作
  • processMessage() 在此状态时,主体所要执行的对应动作的处理方法

然后需要有一个拥有多种状态的主体类,称其为环境类或状态机,既然学习安卓,那就称它为状态机,由于安卓11源码的StateMachine太多,这里就自己写了一个伪代码,如下。

public class StateMachine {

    private State currentState;
    private State initState;

    public StateMachine(State state) {
        initState = state;
        currentState = state;
    }
    
    /*切换状态*/
    public void transitionTo(State state){
        currentState = state;
    }

    public void doSomething() {
        currentState.processMessage(msg);
    }
}

状态机主要的方法会有一个执行动作的方法,和一个切换状态的方法,一般在执行完某个动作之后才会决定需不需要切换状态。具体实现需要更具不同的业务来确定实现多少个状态类,然后给状态机一个新的状态同一个方法就会有不同的处理。

二、wifi框架中的状态机

1.wifi框架中状态机及常用方法

安卓wifi框架中的状态机,是基于状态模式的一个实现,主要继承的就是StateMachine基类,此基类抽象出来并实现了状态机中对状态操作的必要的公共方法,例如,状态的切换、添加/移除状态、状态树、消息传递等。在上一部分中介绍的状态模式的实现是相对较简单的,状态机相当于一个功能更丰富的状态模式的实现。

在wifi框架中的StateMachine里,有一个HandlerSmHandler,其主要用于真正实现状态机的功能型方法,然后状态处理消息也是在SmHandler中的线程中处理,如下图逻辑所示。
【安卓Framework学习】Wifi框架学习之wifi状态机_第1张图片
下面将从状态机初始化、开始启动、状态切换、分发处理状态消息来做代码梳理。

1.1状态机的初始化和启动

When a state machine is created, addState is used to build the hierarchy and setInitialState is used to identify which of these is the initial state. After construction the programmer calls start which initializes and starts the state machine. The first action the StateMachine is to the invoke enter for all of the initial state's hierarchy, starting at its eldest parent. The calls to enter will be done in the context of the StateMachine's Handler, not in the context of the call to start, and they will be invoked before any messages are processed.

以上为安卓11源码中StateMachine的注释,其写的非常详细。大致意思是,当状态机被创建的时候,需要通过addState方法来建立状态树,然后用setInitialState方法指定初始化时的状态,在创建状态机对象后调用状态机的start方法可以初始化并开启状态机。在start方法中,状态机首先就会调用初始化状态所在状态树上所有状态的enter方法,是在状态机的Handler中调用,并且在start方法执行前是不会处理任何消息。

所以在状态机对象创建后,需要通过start方法初始化状态机,那么首先看构造函数都干了什么。

    private void initStateMachine(String name, Looper looper) {
        mName = name;
        mSmHandler = new SmHandler(looper, this);
    }
    
    protected StateMachine(String name) {
        mSmThread = new HandlerThread(name);
        mSmThread.start();
        Looper looper = mSmThread.getLooper();

        initStateMachine(name, looper);
    }
    
    protected StateMachine(String name, Looper looper) {
        initStateMachine(name, looper);
    }

可以看到,构造方法中主要是区分有没有给对应的Looper,然后通过此Looper去创建一个状态机用来执行消息的SmHandler.

由于状态机和前面提到的状态模式不同,状态机是在状态模式的基础上丰富了许多,所以在这里状态机中的状态是以一种树状结构存在,所以下面再看addState方法是怎么构建状态树的。

	/**
	 * 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;
	}

	private class StateInfo {
	    /** The state */
	    State state;
	    /** The parent of this state, null if there is no parent */
	    StateInfo parentStateInfo;
	    /** True when the state has been entered and on the stack */
	    boolean active;
	    /*省略其他部分*/
	}

方法中涉及到了数据结构类StateInfo,此类只有三个变量,将当前状态与父状态联系起来。回到addState方法中,首先会判断parent父状态是否为空,若不为空,则需要去mStateInfo中查找parent对应的StateInfo对象,其中mStateInfoHashMap。然后需要在mStateInfo中查找是否有被添加状态的StateInfo对象,若不存在则创建一个,然后添加到mStateInfo中,并且将新的StateInfo对象重新赋值其代表的当前状态及其父状态以及是否被激活。这样状态树就通过StateInfo对象建立起来了,随后调用setInitialState方法设置一个初始化的状态,如下。

	private final void setInitialState(State initialState) {
	    if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName());
	    mInitialState = initialState;
	}

可以看到setInitialState并没有做其他很多事,只是单纯的将初始化状态记录下来了,保存在mInitialState 变量中。此变量在后续初始化状态树时,会将此状态所在状态树的状态都初始化。然后再调用状态机的start方法,开始初始化状态机。

	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();
	}
	
	private final void completeConstruction() {
	    /**
	     * 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;
	        }
	    }
	    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));
	}

可以看到start方法直接调用了SmHandler.completeConstruction方法。首先就计算了保存在mStateInfo中所有状态对应的树的深度,并取最大深度创建两个StateInfo对象栈。这两个栈在后续切换状态,执行状态的enterexit方法有非常大的作用。再进入到setupInitialStateStack方法中。

	private final void setupInitialStateStack() {
	    StateInfo curStateInfo = mStateInfo.get(mInitialState);
	    for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
	        mTempStateStack[mTempStateStackCount] = curStateInfo;
	        curStateInfo = curStateInfo.parentStateInfo;
	    }
	    // Empty the StateStack
	    mStateStackTopIndex = -1;
	    moveTempStateStackToStateStack();
	}

刚刚通过setInitialState设置的状态在这里用到了,在这个for循环中,将初始状态树整个都存在了mTempStateStack数组中,并且特点是,越在树上层的状态,存在mTempStateStack数组中越靠后。然后进入moveTempStateStackToStateStack.

	private final int moveTempStateStackToStateStack() {
	    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;
	    return startingIndex;
	}

这个方法中,是将刚刚存入mTempStateStack数组的StateInfo对象的顺序倒过来,再存入到mStateStack数组中,这样状态树最顶层节点存在mStateStack数组中的最前面,并且在执行完之后mStateStackTopIndex 指的就是mStateStack数组的最后一个的索引值。

再回到SmHandler.completeConstruction中,最后调用了sendMessageAtFrontOfQueue,给SmHandler发送了一个带有mSmHandlerObj对象的SM_INIT_CMD消息,再看SmHandler.handleMessage方法。

	public final void handleMessage(Message msg) {
	    if (!mHasQuit) {
	        /*省略其他*/
	        State msgProcessedState = null;
	        if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) { 
	            msgProcessedState = 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);
	        }
	        /*省略其他*/
	    }
	}

变量mIsConstructionCompleted 在申明时没有给初始值,那么它就是false,所以会进入到第二个分支,直接看invokeEnterMethods方法。

	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
	}

mStateStackTopIndex存的是在mStateStack最后一个StateInfo对象的索引值,由于传进来的stateStackEnteringIndex为0,那么这里其实是执行了mStateStack数组中存的状态树的所有状态的enter方法。到这里状态机的start方法执行完毕,也完成了所有的初始化动作。

1.2状态机的消息处理及状态切换

上面初始化状态机后,可以看到在状态机中能够存在多个状态或状态树,但是在实际使用过程中不能每次需要切换状态时都去创建一个新的状态对象传入,容易消耗内存。所以在状态机中,添加的状态就会被重复使用,这中间就涉及到状态切换的过程。一般切换过程是伴随在处理消息的过程中,首先来看状态机是如何处理消息的。从状态机的sendMessage方法中开始。

	public void sendMessage(Message msg) {
	    // mSmHandler can be null if the state machine has quit.
	    SmHandler smh = mSmHandler;
	    if (smh == null) return;
	    smh.sendMessage(msg);
	}

最后调入到了SmHandler.sendMessage中,所以又直接跳转到SmHandler.handleMessage方法中。

	public final void handleMessage(Message msg) {
	    if (!mHasQuit) {
	        /*省略部分*/
	        mMsg = msg;
	        /** State that processed the message */
	        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)) {
	            /*省略部分*/
	        } else {
	            /*省略部分*/
	        }
	        performTransitions(msgProcessedState, msg);
	       /*省略部分*/
	    }
	}

在前面完全初始化后mIsConstructionCompleted == true,所以处理消息会执行两个重要的方法processMsgperformTransitions,下面首先进入到processMsg.

	private final State processMsg(Message msg) {
	    StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
	    if (isQuit(msg)) {
	        transitionTo(mQuittingState);
	    } else {
	        while (!curStateInfo.state.processMessage(msg)) {
	            curStateInfo = curStateInfo.parentStateInfo;
	            if (curStateInfo == null) {
	                mSm.unhandledMessage(msg);
	                break;
	            }
	        }
	    }
	    return (curStateInfo != null) ? curStateInfo.state : null;
	}

上面代码删除了不必要的部分,留下关键处理部分。isQuit方法是判断消息是否为一个退出状态机的消息,这暂且不考虑退出的情况。进入while循环,while循环的作用就是从mStateStack栈顶开始调用状态的processMessage方法,直到在这个状态树上有状态确认处理了这个消息,最后返回处理了这条消息的状态。其中,这里一般在某个状态处理消息时,会在processMessage方法中调用transitionTo方法来切换状态,然后再进入到performTransitions.

首先看transitionTo方法。

	private final void transitionTo(IState destState) {
	    if (mTransitionInProgress) {
	        Log.wtf(mSm.mName, "transitionTo called while transition already in progress to " +
	                mDestState + ", new target state=" + destState);
	    }
	    mDestState = (State) destState;
	    if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
	}

切换状态方法中没有做其他的,仅仅是将全局变量mDestState赋值操作而已。然后再看performTransitions,在这里用到了mDestState.

	private void performTransitions(State msgProcessedState, Message msg) {
	    /*省略部分*/
	    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);
	            // flag is cleared in invokeEnterMethods before entering the target state
	            mTransitionInProgress = true;
	            invokeExitMethods(commonStateInfo);
	            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;
	    }
	   /*省略部分*/
	}

上述代码中省略了部分日志打印和不相关代码。首先将刚刚要切换的目标状态赋值给布局变量destState,假设这里在调用transitionTo方法时传入的状态不为空,那么将会进入到死循环中。循环中调用了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;
	    StateInfo curStateInfo = mStateInfo.get(destState);
	    do {
	        mTempStateStack[mTempStateStackCount++] = curStateInfo;
	        curStateInfo = curStateInfo.parentStateInfo;
	    } while ((curStateInfo != null) && !curStateInfo.active);
	    if (mDbg) {
	        mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
	                + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
	    }
	    return curStateInfo;
	}

由于存在状态机中的状态对象是唯一的,所以已经被激活的状态其StateInfo对象会一直保存激活的状态,所以此方法其实是在找要切换到的目标状态mDestState的状态树和当前所处状态的状态树的交点。在while循环中的判断条件!curStateInfo.activetrue时,说明此时的curStateInfo为两个状态树的交点,得到交点状态后返回此交点状态。并且目标状态树的前面未激活的状态也都存入了mTempStateStack中,并且用mTempStateStackCount记录了新存入状态的索引值。其过程如下图。
【安卓Framework学习】Wifi框架学习之wifi状态机_第2张图片
由上图过程可知,返回的焦点状态应为B。进入到invokeExitMethods退出状态的方法中。

	/**
	 * 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();
	        mStateStack[mStateStackTopIndex].active = false;
	        mStateStackTopIndex -= 1;
	    }
	}

由方法的注释可知,从栈顶的状态开始直到公共状态(交点状态),调用这些状态的exit方法,这里需要说明的是,在不满足循环条件的时候,mStateStackTopIndex指向的是交点状态的索引值。如下图。
【安卓Framework学习】Wifi框架学习之wifi状态机_第3张图片
由上图可知,在执行完invokeExitMethods方法后,在mStateStack中,交点状态以下的子状态都执行了exit方法。随后调用moveTempStateStackToStateStack方法,将mTempStateStack中新加进来的状态加入到mStateStack中,过程如下图。
【安卓Framework学习】Wifi框架学习之wifi状态机_第4张图片
最后会调用invokeEnterMethods方法执行切换状态树后新加入的那部分状态的enter方法。

	/**
	 * Invoke the enter method starting at the entering index to top of state stack
	 */
	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
	}

到这里performTransitions方法基本执行完毕了,后面还调用了一个moveDeferredMessageAtFrontOfQueue方法,这个方法是配合deferMessage方法使用的。在切换状态前调用deferMessage,将消息存在一个列表中,然后在转换状态树后,调用moveDeferredMessageAtFrontOfQueue方法,将刚刚存好的消息拿出来,给新的状态树再处理一次。

	private final void deferMessage(Message msg) {
	    if (mDbg) mSm.log("deferMessage: msg=" + msg.what);
	    /* Copy the "msg" to "newMsg" as "msg" will be recycled */
	    Message newMsg = obtainMessage();
	    newMsg.copyFrom(msg);
	    mDeferredMessages.add(newMsg);
	}
	
	private final void moveDeferredMessageAtFrontOfQueue() {
	    for (int i = mDeferredMessages.size() - 1; i >= 0; i--) {
	        Message curMsg = mDeferredMessages.get(i);
	        if (mDbg) mSm.log("moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what);
	        sendMessageAtFrontOfQueue(curMsg);
	    }
	    mDeferredMessages.clear();
	}

至此,状态机的消息处理以及在处理后切换状态树全部执行完。

1.3状态机的退出

上一小节是分析了状态机的消息处理和状态切换,这一部分分析状态机是如何退出的。状态机退出有两个方法quitquitNow,如下图。

	/** @see StateMachine#quit() */
	private final void quit() {
	    if (mDbg) mSm.log("quit:");
	    sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
	}
	/** @see StateMachine#quitNow() */
	private final void quitNow() {
	    if (mDbg) mSm.log("quitNow:");
	    sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
	}

由代码可以看到,本质上两个方法是一样的,只不过在给消息队列发送消息时有所区别quitNow是将退出消息直接插入在消息队列的头部,而quit是将将退出消息直接插在消息队列尾部。由两个方法的名字也能够看得出来,所以只需要分析收到这个退出消息时,状态机是如何处理的。依旧看handleMessage.

	public final void handleMessage(Message msg) {
	    if (!mHasQuit) {
	        /*省略部分*/
	        /** State that processed the message */
	        State msgProcessedState = null;
	        if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) {
	            /** Normal path */
	            msgProcessedState = processMsg(msg);
	        }
	        performTransitions(msgProcessedState, msg);
	        /*省略部分*/
	    }
	}

可以看到,退出消息也是走正常的消息处理逻辑,再进入processMsg.

	private final State processMsg(Message msg) {
	    StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
	    /*省略部分*/
	    if (isQuit(msg)) {
	        transitionTo(mQuittingState);
	    } else {
	        /*省略部分*/
	    }
	    return (curStateInfo != null) ? curStateInfo.state : null;
	}

这里可以知道isQuit这时候判断消息为退出消息时会返回true,在进入isQuit看做了什么判断。

	private final boolean isQuit(Message msg) {
	    return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
	}

确实在quitquitNow中发送的也是SM_QUIT_CMDmSmHandlerObj,因此返回true,这时会将状态切换到退出状态mQuittingState,然后又会进入到performTransitions中。

private void performTransitions(State msgProcessedState, Message msg) {
    /*前面处理消息的逻辑*/
	State destState = mDestState;
    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();
        }
    }
}

由于mQuittingState状态不和任何状态树相交,所以当前被激活的状态都会执行exit方法,然后处理完消息后,会判断当前目标状态是否为退出状态mQuittingState,然后会去调用cleanupAfterQuitting.

	private final void cleanupAfterQuitting() {
	    if (mSm.mSmThread != null) {
	        // If we made the thread then quit looper which stops the thread.
	        getLooper().quit();
	        mSm.mSmThread = null;
	    }
	    mSm.mSmHandler = null;
	    mSm = null;
	    mMsg = null;
	    mLogRecords.cleanup();
	    mStateStack = null;
	    mTempStateStack = null;
	    mStateInfo.clear();
	    mInitialState = null;
	    mDestState = null;
	    mDeferredMessages.clear();
	    mHasQuit = true;
	}

这里可以看到cleanupAfterQuitting方法将所有的状态机内部的相关对象全部清空,后续有任何消息其实也都是没办法发送过来也不会处理,所以也就完全退出状态机了。

2.wifi框架中出现的状态机

2.1 ActiveModeWarden.WifiController

这个状态机由源码注释可以知道,其作用主要是用来管理不同操作模式下的wifi状态(主要是开始和关闭),其中有三个状态DefaultStateDisabledStateEnabledState,其状态树关系为DefaultState是其他两个状态的父状态。

2.2 ClientModeManager.ClientModeStateMachine

由于对wifi有不同的操作(wifi和热点),在ActiveModeWarden类中开启或关闭wifi时,就会调用ClientModeManager类去实际开启和关闭并对上发送广播。ClientModeStateMachine状态机中存在四个状态IdleStateStartedStateScanOnlyModeStateConnectModeState,其中StartedStateScanOnlyModeStateConnectModeState的父状态,IdleState单独为一个状态树。这个状态机主要管理了wifi功能的开和关以及对上发送wifi开关状态广播,对应的热点功能的相关操作在SoftApManager类中的SoftApStateMachine状态机中。

2.3 ClientModeImpl

这个类继承了StateMachine,本身就是一个状态机,其中包括了所有的wifi逻辑操作和连接状态。其主要有8个状态,DefaultStateConnectModeStateL2ConnectedStateObtainingIpStateRoamingStateConnectedStateDisconnectingStateDisconnectedState,其对应的状态树如下图。
【安卓Framework学习】Wifi框架学习之wifi状态机_第5张图片


总结

在安卓wifi框架中状态机使用得非常多,并且状态模式和状态机能够很好与wifi相关业务相结合,其中ClientModeImpl类为wifi框架中的核心状态机,其囊括了所有的wifi连接断开扫描等操作,几乎wifi框架中的核心业务都在其中。

你可能感兴趣的:(android,学习,java)