Android系统中对于WIFI,蓝牙模块运用了状态机来管理状态。搜索了下4.0中的状态机有以下文件:
./frameworks/base/wifi/java/android/net/wifi/WpsStateMachine.java
./frameworks/base/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
./frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java
./frameworks/base/core/java/android/net/dhcp/DhcpStateMachine.java
./frameworks/base/core/java/android/net/DhcpStateMachine.java
./frameworks/base/core/java/android/server/BluetoothAdapterStateMachine.java
以上的类都是继承于./frameworks/base/core/java/com/android/internal/util/StateMachine.java。
系统为了好管理,将一个模块的不同状态封装成State,而StateMachine就是用来管理这些State的。
看一下State类中提供的主要方法有:
@Override
public void enter() {
}
/* (non-Javadoc)
* @see com.android.internal.util.IState#exit()
*/
@Override
public void exit() {
}
/* (non-Javadoc)
* @see com.android.internal.util.IState#processMessage(android.os.Message)
*/
@Override
public boolean processMessage(Message msg) {
return false;
}
@Override
public String getName() {
String name = getClass().getName();
int lastDollar = name.lastIndexOf('$');
return name.substring(lastDollar + 1);
}
再回到StateMachine.java这个文件,在这个文件开始有一大堆的英文说明该类的作用和实现过程。下面结合自己的理解梳理下这段内容:
用法准备条件:首先根据需要要生成我们的State, 比如 private class PowerOff extends State{}(引用源代码中管理Bt的一个状态),我们定义的这个类必须继承State这个类并根据需要实现相应的方法,其中的processMessage这个方法必须实现(如果不重写这个方法,那我们的状态机将没有任何意义)。enter()方法是当我们进入该状态的时候第需要调用的(像程序代码中的构造函数),exit()是退出该状态的时候调用(像析构函数)。
当准备好State后,就在调用addState(State state, State parent)将我们的状态添加进StateMachine。
到这里首先看下StateMachine还有些什么内容。
内部类:SmHandler(大部分处理都是通过这个类完成),ProcessedMessageInfo,ProcessedMessages。在SmHandler中又包含内部类StateInfo,成员StateMachine(mSm),HashMap<State, StateInfo> mStateInfo,mStateStack,mDeferredMessages,State mInitialState, State mDestState, rivate ArrayList<Message> mDeferredMessages。
启动状态机的步骤:
1.首先需要通过addState添加上面准备好的State实例,每一个State都可以添加一个自身的State和一个parentState。add的方法最中是通过调用SmHandler的StateInfo addState(State state, State parent)方法。这个方法会将他的状态以及他的父状态的信息取出来并放到mStateInfo这个hashMap表中的(首先会从mStateInfo中通过State找一下是否存在该状态的相关信息,不存在才会put)。
2.添加完所有的状态后就调用setInitialState设置一个初始状态, 最终调用的是SmHandler的setInitialState给mInitialState赋值。
3当经过1,2步骤后就要调用StateMachine:start()函数启动状态机机,start函数最终调用的是SmHandler:completeConstruction ,这个函数主要是调用exit退出当前状态并更新记录的State的堆栈信息(mStateStack中的State)并掉用State的enter函数,正式进入新的状态。
4.当我们通过StateMachine:sendMessage发送消息的时候,StateMachine就会找到当前的State调用其processMessage来做相应的处理动作。如果当前状态在处理完相应的事务后需要切换到新的状态就需要调用transitionTo(IState destState)设置mDestState的值。
注意:上面说过一个State可能包含一个Parent State,如果在当前的State中没有能处理发送来的msg的时候,就要向上找其父State来处理,依次向上遍历,如果所有的State都不能处理的话就会调用SmHandler:unhandledMessage来处理。
另外两个方法:deferMessage和sendMessageAtFrontOfQueue,在代码中可以通过deferMessage向mDeferredMessages这种存放暂时不处理的消息。在每此处理完新旧状态更新的时候就要调用sendMessageAtFrontOfQueue将mDeferredMessages中的消息优先发送出去(有可能是在A状态deferMessage的消息,切换到B状态后才发送出去,此时就应该B状态去处理这个消息),每次发送完就会清空mDeferredMessages一次。
图表解释:
mP0
/ \
mP1 mS0
/ \
mS2 mS1
/ \ \
mS3 mS4 mS5 ---> initial state
如上图,添加了mS5至mP0的8个状态, 其中mS1是mS5的父状态,mP1是mS1的父状态,依次类推。上图可以看出mS5是初始状态,当启用状态机的收回调用StateMachine:start()----->SmHandler:completeConstruction(),这函数里面会调用invokeEnterMethods从而依次调用mP0--mP1--mS1--mS5的enter函数,并将他们每一个对应的StateInfo的active置为true。最后调用的是mS5,所以当前的状态进入mS5。此时如果外面有消息通过StateMachine:sendMessage发送进来,首先就会mS5----mP0依次调用processMessage的方法来出来当前消息直到处理成功(如果mS5就处理成功就跳出了,就不会进入mS1, 以此类推)。
假设现在mP0----mS5处理发送来的消息成功,并且transitionTo(mS4),往上遍历发现有共同祖先mP1,那么就会依次调用mS5---mS1的exit函数,依次调用mS2---mS4的enter函数从而进入mS4状态。现在处于active的状态就是mS4,mS2,mP1,mP0了,下次来消息的时候就该mS4来处理了。
StateMachine的基本原理就是这样,StateMachine基本线路就在SmHandler的中handleMessage函数。
#################################
Bluetooth状态转换图如下:
[html] view plaincopy(BluetootOn)<----------------------<-
40 * | ^ -------------------->- |
41 * | | | |
42 * TURN_OFF | | SCAN_MODE_CHANGED m1 | | USER_TURN_ON
43 * AIRPLANE_MODE_ON | | | |
44 * V | | |
45 * (Switching) (PerProcessState)
46 * | ^ | |
47 * POWER_STATE_CHANGED & | | TURN_ON(_CONTINUE) | |
48 * ALL_DEVICES_DISCONNECTED | | m2 | |
49 * V |------------------------< | SCAN_MODE_CHANGED
50 * (HotOff)-------------------------->- PER_PROCESS_TURN_ON
51 * / ^
52 * / | SERVICE_RECORD_LOADED
53 * | |
54 * TURN_COLD | (Warmup)
55 * ^
56 * | TURN_HOT/TURN_ON
57 * | | AIRPLANE_MODE_OFF(when Bluetooth was on before)
58 * V |
59 * (PowerOff) <----- initial state
涉及到的文件:
./frameworks/base/core/java/android/server/BluetoothAdapterStateMachine.java
./frameworks/base/core/java/com/android/internal/util/StateMachine.java
BluetoothAdapterStateMachine继承了StateMachine
先看状态机如何被启动的:
基本流程就是这样.
1.BluetoothAdapterStateMachine的初始化动作:
[java] view plaincopy
mBluetoothOn = new BluetoothOn();
mSwitching = new Switching();
mHotOff = new HotOff();
mWarmUp = new WarmUp();
mPowerOff = new PowerOff();
mPerProcessState = new PerProcessState();
addState(mBluetoothOn);
addState(mSwitching);
addState(mHotOff);
addState(mWarmUp);
addState(mPowerOff);
addState(mPerProcessState);
setInitialState(mPowerOff);
mPublicState = BluetoothAdapter.STATE_OFF;
在BluetoothAdapterStateMachine的构造函数中添加了,Switching、HotOff()、WarnUp、PowerOff、PerProcessState等几种状态。
并设置了初始状态为PowerOff。有一个mPublicState的变量,这个变量保存状态机最终向外公布的状态。
由上面可以看出,添加的这几个状态都是独立的,没有父状态, 所以转换相对就比较简单了。
2.状态机的转换。
首先进入了PowerOff的状态, 有用户打打开蓝牙,或设置了了开机启动蓝牙,就会收到USER_TURN_ON的消息。这里以开机启动蓝牙为例, 阐释消息如何传递的。
SystemServer:bluetooth.enable();------>BluetoothService:enable()------>mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);------>
StateMachine:sendMessage()------>mSmHandler.sendMessage(obtainMessage(what,obj));(SmHandler为StateMachine的内部类)--------->SmHandler:handleMessage-------->
SmHandler:processMsg()------->BluetoothAdapterStateMachine:PowerOff:processMessage()。(一)
在PowerOff:processMessage中处理USER_TURN_ON这个消息,做了一些蓝牙启动的准备的动作(调用prepareBluetooth()) ,并且transitionTo(mWarmUp);并且设置了目标状态为WarmUp,这个函数最终是给
SmHandler:mDestState赋值 赋值后SmHandler:mDestState = (mWarmUp)。并且deferMessage(obtainMessage(TURN_ON_CONTINUE));(这个函数会将消息放入SmHandle:mDeferredMessages
中,当实现状态切换的时候会全部派发出去。mDeferredMessages是一个ArrayList)。
接(一)。------->SmHandler:performTransitions----->BluetoothAdapterStateMachine:PowerOff:exit(),BluetoothAdapterStateMachine:WarmUp:enter()(进入了WarmUp了),moveDeferredMessageAtFrontOfQueue();(这个函数
会将mDeferredMessages中存放的消息全部发送出去并清空,比如在现在的流程这里有个TURN_ON_CONTINUE)。----->和上面USER_TURN_ON的消息传递流程一样------>BluetoothAdapterStateMachine:WarmUp:processMessage()(处理TURN_ON_CONTINUE)------>WarmUp:deferMessage(message)(该message为TURN_ON_CONTINUE);(这里是为了等待prepareBluetooth()
这个函数完成,当prepareBluetooth()函数完成后将会发送一个SERVICE_RECORD_LOADED的消息)------>SmHandler:performTransitions()------------->BluetoothAdapterStateMachine:Warmup:exit(),
BluetoothAdapterStateMachine:HotOff:enter()(进入了HotOff了),moveDeferredMessageAtFrontOfQueue();(将会发送TURN_ON_CONTINUE消息)------------>
BluetoothAdapterStateMachine:HotOff:processMessage()-------->mBluetoothService.switchConnectable(true);(这个函数里面发送了一个BluetoothAdapterStateMachine.SCAN_MODE_CHANGED的消息),transitionTo(mSwitching);(设置了目标状态SmHandler:mDestState的值为Switching)-------->SmHandler:performTransitions()---------->
BluetoothAdapterStateMachine:WarmUp:exit(),BluetoothAdapterStateMachine:HotOff:enter()(进入了HotOff了),moveDeferredMessageAtFrontOfQueue();---------->和上面USER_TURN_ON的消息传递流程一样------------->
BluetoothAdapterStateMachine:Switching:processMessage()(处理SCAN_MODE_CHANGED)-------->做一些蓝牙的启动工作后,transitionTo(mBluetoothOn);(目标状态为BluetoothOn)------->SmHandler:performTransitions()---->
BluetoothAdapterStateMachine:Switching:exit(),BluetoothAdapterStateMachine:BluetoothOn:enter()(进入了BluetoothOn状态了),moveDeferredMessageAtFrontOfQueue();。
蓝牙的整个启动过程状态机就是这样,由于不涉及到父状态,所以整个过程还是比较简单的,其他过程可以类比。对于这边文章大家可以参考我的前一篇 Android中的状态机。