状态模式的简单模板

本文通过分析通话过程中的声音输出设备切换,介绍了状态模式的基本原理及应用。状态模式通过封装不同的状态逻辑,简化了if-else判断,提高了代码的可维护性和扩展性。文章详细解释了CallAudioModeStateMachine的实现,包括状态的初始化、默认状态设置、状态切换过程等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:在分析通话过程中声音输出设备切换的时候,我们发现每一种设备,它内部都封装了一套自己的逻辑处理,当我们切换到某一个设备时,就会走相应的流程,这就是典型的状态模式;

状态模式简化了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 ;
}

 

总结:上面是一个极其简单的状态模式的例子,它里面还可以加很多其他的逻辑,以后有机会会继续填充它;

完结,散花!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

renshuguo123723

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值