前言:在分析通话过程中声音输出设备切换的时候,我们发现每一种设备,它内部都封装了一套自己的逻辑处理,当我们切换到某一个设备时,就会走相应的流程,这就是典型的状态模式;
状态模式简化了if,else的逻辑判断,可维护性大大加强,后期添加功能时也是十分的方便;
我们今天就来写个最简单的状态模式的模板,后续再在这个简单的模板上面添加更加复杂的逻辑;
还是以CallAudioManager管理所有的音频状态为例:
(1)初始化CallAudioModeStateMachine
①CallAudioModeStateMachine是管理所有状态的容器,CallAudioModeStateMachine对象在被创建的时候,会初始化一个默认状态,看下CallAudioModeStateMachine的构造函数:
public CallAudioModeStateMachine() {
//将所有的状态添加进容器
addState(mUnfocusedState);
addState(mRingingFocusState);
addState(mSimCallFocusState);
addState(mVoipCallFocusState);
addState(mOtherFocusState);
//设置mUnfocusedState为当前的初始化状态
setInitialState(mUnfocusedState);
}
②再看下CallAudioModeStateMachine是如何填满容器的:
private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();
private final StateInfo addState(State state) {
StateInfo stateInfo = mStateInfo.get(state);
if (stateInfo == null) {
//创建StateInfo
stateInfo = new StateInfo();
//以State为key,保存stateInfo
mStateInfo.put(state, stateInfo);
}
//State与stateInfo进行绑定
stateInfo.state = state;
//active表示当前的状态不活跃
stateInfo.active = false;
return stateInfo;
}
使用Map来保存所有的音频状态,stateInfo是对State的封装;
(2)给CallAudioModeStateMachine设置默认状态
①看CallAudioModeStateMachine是如何来设置默认状态以及以及初始化的:
private State mCurrentState;
private final void setInitialState(State initialState) {
mCurrentState = initialState;
}
使用mInitialState保存了初始化的状态;
②再看下start()函数:
private void start(){
StateInfo curStateInfo = mStateInfo.get(mCurrentState);
//调用mUnfocusedState.enter()进行初始化
curStateInfo.state.enter();
//表示当前的State是活跃的
curStateInfo.active = true;
}
代码走到了mUnfocusedState.enter(),已经到了具体的状态切换了;
(3)状态切换
切换的接口写在在CallAudioManager里:
void setAudioMode(int mode) {
switch (mode) {
case CallAudioState.ROUTE_BLUETOOTH:
mCallAudioModeStateMachine.switchAudioRoute(
CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH);
return;
case CallAudioState.ROUTE_SPEAKER:
mCallAudioModeStateMachine.switchAudioRoute(
CallAudioRouteStateMachine.USER_SWITCH_SPEAKER);
return;
case CallAudioState.ROUTE_WIRED_HEADSET:
mCallAudioModeStateMachine.switchAudioRoute(
CallAudioRouteStateMachine.USER_SWITCH_HEADSET);
return;
case CallAudioState.ROUTE_EARPIECE:
mCallAudioModeStateMachine.switchAudioRoute(
CallAudioRouteStateMachine.USER_SWITCH_EARPIECE);
return;
default:
Log.wtf(this, "Invalid route specified: %d", route);
}
}
具体的切换操作由CallAudioModeStateMachine来实现:
public void switchAudioRoute(int audioRoute){
StateInfo curStateInfo = mCurrentState ;
//得到要切换的目标状态
curStateInfo.state.processRoute(audioRoute) ;
//开始切换
performTransitions();
}
下一个音频状态的确定:
private class UnfocusedState extends BaseState {
@Override
public void enter() {
............
}
@Override
public boolean processRoute(int audioRoute) {
switch (audioRoute) {
case ROUTE_BLUETOOTH:
// Do nothing.
return HANDLED;
case ROUTE_SPEAKER:
// Do nothing.
return HANDLED;
case ROUTE_WIRED_HEADSET:
// Do nothing.
return HANDLED;
case ROUTE_EARPIECE:
transitionTo(mRingingFocusState);
return HANDLED;
}
}
}
//mDestStat为切换到的目标状态
private State mDestState;
private final void transitionTo(IState destState) {
mDestState = (State) destState;
}
具体要切换到哪个对象由UnfocusedState去选择,当然,如果指定了切换对象的话,比如用户指定了"要切换到麦克风",那这一步可以省略,直接切换到麦克风即可;
但是如果用户只是笼统的说了一句"切换到其他对象",那这个切换对象就由于我们来控制了;
音频切换:
private void performTransitions(){
State destState = mDestState;
StateInfo destStateInfo = mStateInfo.get(destState);
StateInfo curStateInfo = mStateInfo.get(mCurrentState);
if(curStateInfo != destStateInfo){
//UnfocusedState要先退出
curStateInfo.state.exit();
//并且设置UnfocusedState是不活跃的
curStateInfo.active = false;
}
//进入下一个目标音频状态
destStateInfo.state.enter();
//设置这个状态是活跃的
destStateInfo.active = true;
//切换完成,目标音频状态成为当前的音频状态
mCurrentState = destState ;
}
总结:上面是一个极其简单的状态模式的例子,它里面还可以加很多其他的逻辑,以后有机会会继续填充它;
完结,散花!