IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 【安卓R 源码】从 Telecom CallAudioModeStateMachine详解StateMachine状态机源码 -> 正文阅读

[移动开发]【安卓R 源码】从 Telecom CallAudioModeStateMachine详解StateMachine状态机源码

状态机是将对象的状态与行为封装在一起;可以解决庞大的分支语句带来程序阅读性差和不便于进行扩展问题,使整个结构变得更加清晰明了,降低程序管理的复杂性提高灵活度。Android系统的StateMachine机制是一个State模式的应用,StateMachine是一个分层处理消息的状态机,并且是能够有分层排列状态。

以 Telecom 的CallAudioModeStateMachine 作为分析:

1. 状态机的构造函数

  • /packages/services/Telecomm/src/com/android/server/telecom/CallAudioModeStateMachine.java
public class CallAudioModeStateMachine extends StateMachine {

// 调用如下接口去初始化对象了
    public static class Factory {
        public CallAudioModeStateMachine create(SystemStateHelper systemStateHelper,
                AudioManager am) {
            return new CallAudioModeStateMachine(systemStateHelper, am);
        }
    }

看下构造函数

    public CallAudioModeStateMachine(SystemStateHelper systemStateHelper,
            AudioManager audioManager) {

// 传给父类一个参数:name
        super(CallAudioModeStateMachine.class.getSimpleName());
        mAudioManager = audioManager;
        mSystemStateHelper = systemStateHelper;
        mMostRecentMode = AudioManager.MODE_NORMAL;

// 构建 states 状态
        createStates();
    }

看下父类StateMachine 的构造函数

  • /frameworks/base/core/java/com/android/internal/util/StateMachine.java

在?StateMachine中,开启了一个线程HandlerThread,其对应的Handler为SmHandler。?

    @UnsupportedAppUsage
    protected StateMachine(String name) {
        mSmThread = new HandlerThread(name);
// 开启这个线程
        mSmThread.start();
// 获取 Looper对象
        Looper looper = mSmThread.getLooper();

        initStateMachine(name, looper);
    }

----------------
    private void initStateMachine(String name, Looper looper) {
        mName = name;

// 创建了 SmHandler 对象
        mSmHandler = new SmHandler(looper, this);
    }

SmHandler构造函数

  • SmHandler构造方法中,向状态机中添加了两个状态:一个状态为状态机的暂停状态mHaltingState、一个状态为状态机的退出状态mQuittingState
    private static class SmHandler extends Handler {

// 2 个初始化的状态
        private class HaltingState extends State {
            @Override
            public boolean processMessage(Message msg) {
                mSm.haltedProcessMessage(msg);
                return true;
            }
        }

        /**
         * State entered when a valid quit message is handled.
         */
        private class QuittingState extends State {
            @Override
            public boolean processMessage(Message msg) {
                return NOT_HANDLED;
            }
        }

        private SmHandler(Looper looper, StateMachine sm) {
            super(looper);
            mSm = sm;

// 增加2 个状态 ,父亲state为空
            addState(mHaltingState, null);
            addState(mQuittingState, null);
        }
  • mHaltingState状态,顾名思义让状态机暂停,其对应的processMessage(Message msg)方法,返回值为true,将消息消费掉,但不处理消息。从而使状态机状态停顿到mHaltingState状态
  • mQuittingState状态,若进入该状态, 状态机将退出。HandlerThread线程对应的Looper将退出,HandlerThread线程会被销毁,所有加入到状态机的状态被清空。
                if (destState != null) {
                    if (destState == mQuittingState) {
                        /**
                         * Call onQuitting to let subclasses cleanup.
                         */
                        mSm.onQuitting();
                        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;
                }

2. 启动状态机

继续看?构建 states 状态 createStates()

  • /packages/services/Telecomm/src/com/android/server/telecom/CallAudioModeStateMachine.java
    private void createStates() {
// 1. 增加如下状态
        addState(mUnfocusedState);
        addState(mRingingFocusState);
        addState(mSimCallFocusState);
        addState(mVoipCallFocusState);
        addState(mAudioProcessingFocusState);
        addState(mOtherFocusState);

// 2. 设置初始状态为 UnfocusedState
        setInitialState(mUnfocusedState);

/// 3. 启动状态机
        start();

// 4. 发送消息去初始化
        sendMessage(INITIALIZE, new MessageArgs.Builder()
                .setHasActiveOrDialingCalls(false)
                .setHasRingingCalls(false)
                .setHasHoldingCalls(false)
                .setIsTonePlaying(false)
                .setForegroundCallIsVoip(false)
                .setSession(Log.createSubsession())
                .build());
    }

// 1. 增加如下状态

    @UnsupportedAppUsage
    public final void addState(State state) {

// 调用 SmHandler的 addState函数
// 父亲state 为null
        mSmHandler.addState(state, null);
    }

---------
        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;
// 如果parent 不为空的话,递归保存其父节点
            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);
// 此时 stateInfo为空
            if (stateInfo == null) {
                stateInfo = new StateInfo();

//将全部6 种状态,保存到 mStateInfo散列表中
                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;
        }

// 2. 设置初始状态为 UnfocusedState

    @UnsupportedAppUsage
    public final void setInitialState(State initialState) {
        mSmHandler.setInitialState(initialState);
    }

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

// 设置初始状态 mInitialState
            mInitialState = initialState;
        }

// 3. 启动状态机 start()

?mStateStack与mTempStateStack为两个用数组实现的堆栈。这两个堆栈的最大长度,即为maxDepth。其用来存储当前活跃状态与当前活跃状态的父状态、父父状态、...等


setupInitialStateStack();完成状态的初始化,将当前的活跃状态放入到mStateStack堆栈中。

    @UnsupportedAppUsage
    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;

// 由于没有父亲节点,这里 maxDepth 为 1
            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");
        }

setupInitialStateStack

        private final void setupInitialStateStack() {
            if (mDbg) {
                mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName());
            }

// 获取初始的状态 mUnfocusedState,将其保存在 mTempStateStack数组中
            StateInfo curStateInfo = mStateInfo.get(mInitialState);
            for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
                mTempStateStack[mTempStateStackCount] = curStateInfo;
                curStateInfo = curStateInfo.parentStateInfo;
            }

            // Empty the StateStack
            mStateStackTopIndex = -1;

            moveTempStateStackToStateStack();
        }
------------------
// 将 临时的栈放到 mStateStack 栈中

        private final int moveTempStateStackToStateStack() {
            int startingIndex = mStateStackTopIndex + 1;
            int i = mTempStateStackCount - 1;
            int j = startingIndex;
            while (i >= 0) {
                if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
                mStateStack[j] = mTempStateStack[i];
                j += 1;
                i -= 1;
            }

            mStateStackTopIndex = j - 1;
            if (mDbg) {
                mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
                        + ",startingIndex=" + startingIndex + ",Top="
                        + mStateStack[mStateStackTopIndex].state.getName());
            }
            return startingIndex;
        }

发送初始化消息
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj))

    protected final void sendMessageAtFrontOfQueue(int what) {
        // mSmHandler can be null if the state machine has quit.
        SmHandler smh = mSmHandler;
        if (smh == null) return;

        smh.sendMessageAtFrontOfQueue(obtainMessage(what));
    }

--------------
// 消息处理
        @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 || (mMsg.what == SM_QUIT_CMD)) {
                    /** Normal path */
                    msgProcessedState = processMsg(msg);

// mIsConstructionCompleted 初始值为false
// 然后这里设置为true,就可以走到 上面的条件中去处理消息 processMsg
                } 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);
                }
                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);
                }
            }
        }

------------------
        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());

// 进入到 mUnfocusedState的 enter方法

                mStateStack[i].state.enter();
                mStateStack[i].active = true;
            }
            mTransitionInProgress = false; // ensure flag set to false if no methods called
        }

// 4. 发送消息去初始化
? ? ? ? sendMessage(INITIALIZE, new MessageArgs.Builder

    @UnsupportedAppUsage
    public void sendMessage(int what, Object obj) {
        // mSmHandler can be null if the state machine has quit.
        SmHandler smh = mSmHandler;
        if (smh == null) return;

        smh.sendMessage(obtainMessage(what, obj));
    }

----------
// SmHandler 去处理消息

     @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;
// 因为第一次初始化 SM_INIT_CMD 设置了 mIsConstructionCompleted为true
// 走到如下方法
                if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) {
                    /** Normal path */
                    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);
                }
                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);
                }
            }
        }

----------------
        private final State processMsg(Message msg) {

// 获取初始的状态 UnfocusedState 的 StateInfo
            StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
            if (mDbg) {
                mSm.log("processMsg: " + curStateInfo.state.getName());
            }

            if (isQuit(msg)) {
                transitionTo(mQuittingState);
            } else {

// 获取 UnfocusedState然后去处理消息
// 如果processMessage返回 false,则继续找到父节点继续处理
 
                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;
        }

UnfocusedState 处理消息??INITIALIZE,设置?mIsInitialized 为true

// 继承于 BaseState
    private class UnfocusedState extends BaseState {

    private class BaseState extends State {
        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {

                case INITIALIZE:

// 这里去设置 mIsInitialized 为true
                    mIsInitialized = true;

//     public static final boolean HANDLED = true;
// 返回时父类的 true,即在父类中不往下处理

                    return HANDLED;

3. 来电状态切换到?RingingFocusState 流程

如果有来电的话:

  • /packages/services/Telecomm/src/com/android/server/telecom/CallAudioManager.java

消息为:NEW_RINGING_CALL?

    private void onCallEnteringRinging() {
        if (mRingingCalls.size() == 1) {
            mCallAudioModeStateMachine.sendMessageWithArgs(
                    CallAudioModeStateMachine.NEW_RINGING_CALL,
                    makeArgsForModeStateMachine());
        }
    }
  • /packages/services/Telecomm/src/com/android/server/telecom/CallAudioModeStateMachine.java
    public void sendMessageWithArgs(int messageCode, MessageArgs args) {
        sendMessage(messageCode, args);
    }
  • /frameworks/base/core/java/com/android/internal/util/StateMachine.java
    @UnsupportedAppUsage
    public void sendMessage(int what, Object obj) {
        // mSmHandler can be null if the state machine has quit.
        SmHandler smh = mSmHandler;
        if (smh == null) return;

        smh.sendMessage(obtainMessage(what, obj));
    }

--------
        @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 || (mMsg.what == SM_QUIT_CMD)) {
                    /** Normal path */

// 1. 这里先去处理消息
                    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);
                }
// 2. 处理消息之后,调用状态机的 enter 方法
                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);
                }
            }
        }

------------
        private final State processMsg(Message msg) {
            StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
            if (mDbg) {
                mSm.log("processMsg: " + curStateInfo.state.getName());
            }

            if (isQuit(msg)) {
                transitionTo(mQuittingState);
            } else {
                while (!curStateInfo.state.processMessage(msg)) {



  • /packages/services/Telecomm/src/com/android/server/telecom/CallAudioModeStateMachine.java
    private class UnfocusedState extends BaseState {
        @Override

        @Override
        public boolean processMessage(Message msg) {
            if (super.processMessage(msg) == HANDLED) {
                return HANDLED;
            }
            MessageArgs args = (MessageArgs) msg.obj;
            switch (msg.what) {

                case NEW_RINGING_CALL:

// 变化状态为 RingingFocusState
                    transitionTo(mRingingFocusState);
                    return HANDLED;

transitionTo(mRingingFocusState)

    @UnsupportedAppUsage
    public final void transitionTo(IState destState) {
        mSmHandler.transitionTo(destState);
    }

----------------
       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 为 mRingingFocusState
            mDestState = (State) destState;
            if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
        }

// 2. 处理消息之后,调用状态机的 enter 方法
performTransitions

        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);
            }

// 此时 destState 为 RingingFocusState
            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.
                     */
// 1, 将RingingFocusState 先保存到临时栈中
                    StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                    // flag is cleared in invokeEnterMethods before entering the target state
                    mTransitionInProgress = true;

// 2. 触发UnFocusState 的Exit 退出方法
                    invokeExitMethods(commonStateInfo);

// 3. 将临时栈的数据放到 StateStack 中
                    int stateStackEnteringIndex = moveTempStateStackToStateStack();

4. 执行 RingingFocusState的 enter 方法,并设置标志位 active 为 true
                    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();
                }
            }
        }

4. 执行 RingingFocusState的 enter 方法

    private class RingingFocusState extends BaseState {
        // Keeps track of whether we're ringing with audio focus or if we've just entered the state
        // without acquiring focus because of a silent ringtone or something.
        private boolean mHasFocus = false;

        private void tryStartRinging() {
            if (mHasFocus && mCallAudioManager.isRingtonePlaying()) {
                Log.i(LOG_TAG, "RingingFocusState#tryStartRinging -- audio focus previously"
                        + " acquired and ringtone already playing -- skipping.");
                return;
            }

            if (mCallAudioManager.startRinging()) {
                mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_RING,
                        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
                // Do not set MODE_RINGTONE if we were previously in the CALL_SCREENING mode -- this
                // trips up the audio system.
                if (mAudioManager.getMode() != AudioManager.MODE_CALL_SCREENING) {
                    mAudioManager.setMode(AudioManager.MODE_RINGTONE);
                }
                mCallAudioManager.setCallAudioRouteFocusState(
                        CallAudioRouteStateMachine.RINGING_FOCUS);
                mHasFocus = true;
            } else {
                Log.i(LOG_TAG, "RINGING state, try start ringing but not acquiring audio focus");
            }
        }

// 进入到 enter 方法,开始响铃
        @Override
        public void enter() {
            Log.i(LOG_TAG, "Audio focus entering RINGING state");
            tryStartRinging();
            mCallAudioManager.stopCallWaiting();
        }

参考:

一文详解 Android状态机StateMachine 使用方式及实现原理_bjxiaxueliang的博客-CSDN博客_android statemachine 使用

Android状态机源码分析_快乐安卓的博客-CSDN博客

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-23 10:58:30  更:2022-04-23 10:58:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 23:31:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码