【Android FrameWork】延伸阅读:MediaRecorder状态机

MediaRecorder状态机

MediaRecorder的状态机是Android多媒体框架中分层管控+状态校验+原子性转换的核心设计,其实现贯穿Java层、JNI/Native层、MediaServer服务层,通过"每层维护独立状态变量+操作前严格校验+跨层状态同步"保证状态转换的合法性,避免非法操作导致的资源泄漏或功能异常。

以下从实现层面拆解核心机制:

核心设计

MediaRecorder的状态机并非单一模块实现,而是三层联动的架构,每层维护自己的状态变量,且上层状态转换必须同步到下层,最终以MediaServer服务端的状态为"最终权威"。

层级 状态存储载体 核心作用
Java层 私有枚举变量mState 对外暴露API的第一层校验,屏蔽底层细节,给开发者反馈IllegalStateException
JNI/Native层 MediaRecorder.cppmCurrentState 桥接Java层与MediaServer,同步Java层状态,二次校验避免非法跨进程调用
MediaServer层 StagefrightRecordermState 最终状态管控,所有音视频采集/编码操作的实际状态校验,防止跨进程异常

1. Java层:状态枚举+操作前校验(最外层屏障)

(1)状态变量的定义

MediaRecorder的Java层(android.media.MediaRecorder)通过私有枚举类定义所有合法状态,核心代码片段如下:

java 复制代码
// 核心状态枚举(简化版)
private enum State {
    IDLE,            // 初始状态
    INITIALIZED,     // 设置音/视频源后
    DATA_SOURCE_CONFIGURED, // 配置输出格式/编码后
    PREPARED,        // prepare()完成后
    RECORDING,       // 录制中
    PAUSED,          // 暂停中
    STOPPED,         // 停止录制后
    ERROR,           // 错误状态
    RELEASED         // 资源释放后
}

// 私有状态变量,仅内部可修改
private State mState = State.IDLE;
(2)核心机制:操作前的状态校验

所有对外API(如start()setAudioSource()prepare())调用前,都会先执行状态合法性校验 ,校验失败则直接抛出IllegalStateException,这是开发者最常遇到的异常来源。

核心校验逻辑示例(简化版):

java 复制代码
// 通用状态校验方法
private void checkState(State... allowedStates) {
    for (State s : allowedStates) {
        if (mState == s) {
            return;
        }
    }
    // 状态不合法,抛出异常(包含当前状态和允许的状态,便于调试)
    throw new IllegalStateException(
        "Invalid state " + mState + ", expected one of " + Arrays.toString(allowedStates)
    );
}

// 示例:start()方法的状态校验
public void start() throws IllegalStateException {
    // start()仅允许在PREPARED状态调用
    checkState(State.PREPARED);
    try {
        // 调用JNI层native_start()
        native_start();
        // 状态原子转换为RECORDING
        mState = State.RECORDING;
    } catch (Exception e) {
        // 失败则转入ERROR状态
        mState = State.ERROR;
        throw e;
    }
}
(3)状态转换的原子性

Java层通过synchronized关键字保证状态转换的原子性(避免多线程下状态错乱),所有修改mState的操作都在同步代码块中执行:

java 复制代码
private final Object mLock = new Object();

private void transitionTo(State newState) {
    synchronized (mLock) {
        mState = newState;
    }
}

2. JNI/Native层:状态同步+跨进程桥接

Java层的状态会通过JNI同步到Native层(android_media_MediaRecorder.cpp),Native层维护独立的状态变量,避免Java层状态与底层服务状态不一致。

(1)Native层状态变量
cpp 复制代码
// Native层MediaRecorder类(简化版)
struct MediaRecorder {
    // Native层状态枚举(与Java层一一对应)
    enum State {
        STATE_IDLE,
        STATE_INITIALIZED,
        STATE_DATA_SOURCE_CONFIGURED,
        STATE_PREPARED,
        STATE_RECORDING,
        // ... 其他状态
        STATE_ERROR
    };
    State mCurrentState; // Native层状态变量
    sp<IMediaRecorder> mMediaRecorder; // 指向MediaServer的Binder接口
};
(2)状态同步逻辑

JNI方法调用时,先将Java层的状态同步到Native层,再执行底层操作:

cpp 复制代码
// JNI层native_setAudioSource()示例
static void android_media_MediaRecorder_setAudioSource(JNIEnv *env, jobject thiz, jint source) {
    // 获取Native层MediaRecorder实例
    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
    if (mr == NULL) return;

    // 校验Native层当前状态(仅允许IDLE状态设置音频源)
    if (mr->mCurrentState != MediaRecorder::STATE_IDLE) {
        jniThrowIllegalStateException(env, "setAudioSource called in wrong state");
        return;
    }

    // 调用MediaServer的setAudioSource接口
    status_t ret = mr->mMediaRecorder->setAudioSource((audio_source_t)source);
    if (ret == NO_ERROR) {
        // 同步更新Native层状态为INITIALIZED
        mr->mCurrentState = MediaRecorder::STATE_INITIALIZED;
        // (Java层会自行更新状态,此处仅保证Native层与之一致)
    } else {
        mr->mCurrentState = MediaRecorder::STATE_ERROR;
        jniThrowException(env, "android/media/MediaRecorder$RecorderException", "setAudioSource failed");
    }
}

3. MediaServer层:最终状态管控(跨进程安全)

MediaRecorder的实际录制逻辑运行在MediaServer进程(独立于应用进程),服务端通过StagefrightRecorder(Android原生实现)维护最终的状态,所有操作必须通过Binder接口调用,且服务端会再次校验状态,防止应用层"伪造"状态调用。

(1)服务端状态变量
cpp 复制代码
// MediaServer层StagefrightRecorder.h(简化版)
class StagefrightRecorder : public MediaRecorderBase {
public:
    enum State {
        UNINITIALIZED,
        INITIALIZED,
        CONFIGURED,
        PREPARED,
        RECORDING,
        PAUSED,
        STOPPED
    };
    State mState; // 服务端状态变量
};
(2)服务端状态校验

startRecording()为例,服务端会先校验状态,再启动采集/编码:

cpp 复制代码
// StagefrightRecorder.cpp
status_t StagefrightRecorder::startRecording() {
    Mutex::Autolock _l(mLock);

    // 服务端最终校验:仅允许PREPARED状态启动录制
    if (mState != PREPARED) {
        ALOGE("startRecording called in state %d", mState);
        return INVALID_OPERATION;
    }

    // 启动音频采集(AudioRecord)、视频采集(Camera)、编码器(MediaCodec)
    status_t ret = startAudioRecording();
    if (ret != NO_ERROR) return ret;
    ret = startVideoRecording();
    if (ret != NO_ERROR) return ret;

    // 更新服务端状态为RECORDING
    mState = RECORDING;
    return OK;
}

辅助机制

状态机的稳定性依赖"异常时的状态重置",任何层级发生错误(如编码失败、采集设备被占用、IO错误),都会触发状态回滚到ERROR,并向上层反馈异常:

1. 错误状态的传播

java 复制代码
// Java层错误处理示例
private void handleError(int what, int extra) {
    synchronized (mLock) {
        mState = State.ERROR; // 状态回滚到ERROR
    }
    // 回调开发者注册的OnErrorListener
    if (mErrorListener != null) {
        mErrorListener.onError(this, what, extra);
    }
}

2. 状态重置(reset()/release())

reset()release()是恢复状态的核心方法,会逐层重置状态

  • reset():将Java层状态恢复到IDLE,Native层和MediaServer层同步重置为初始状态,保留MediaRecorder实例;
  • release():彻底释放所有资源(AudioRecord、Camera、MediaCodec),将状态置为RELEASED,实例不可再用。

核心代码示例:

java 复制代码
public void reset() throws IllegalStateException {
    synchronized (mLock) {
        // 仅允许非RELEASED状态调用reset()
        checkState(State.IDLE, State.INITIALIZED, State.DATA_SOURCE_CONFIGURED, 
                   State.PREPARED, State.RECORDING, State.PAUSED, State.STOPPED, State.ERROR);
        native_reset(); // 重置Native层和MediaServer层状态
        mState = State.IDLE; // Java层恢复到初始状态
    }
}

核心设计模式

MediaRecorder的状态机本质是状态模式(State Pattern) 的隐式实现:

  • 没有显式定义每个状态的子类,而是通过"状态变量+条件判断"封装不同状态下的行为;
  • 每个操作(如start()prepare())的行为依赖当前状态,状态不合法则直接拒绝;
  • 状态转换由框架内部管控,开发者只需遵循公开的状态转换规则,无需关心底层细节。

关键注意点

  1. 状态校验的优先级:Java层校验是"快速失败",Native层和MediaServer层校验是"最终保障",即使绕过Java层反射修改状态,MediaServer也会拒绝非法操作;
  2. 多线程安全mState的修改和读取都加锁,避免多线程下的状态错乱(如主线程调用start(),子线程调用stop());
  3. 错误状态的不可逆性 :进入ERROR状态后,必须调用reset()恢复到IDLE,否则所有操作都会失败。

总结

MediaRecorder的状态机实现核心是:

  • 分层管控:Java层(对外)、Native层(桥接)、MediaServer层(实际执行)各维护状态,层层校验;
  • 原子转换:状态修改加锁,避免多线程/跨进程下的不一致;
  • 异常回滚:错误时自动转入ERROR状态,防止非法操作持续;
  • 极简暴露 :对外仅暴露状态转换规则,底层实现完全封装。
相关推荐
陌sr,2 小时前
关于TMK的生成及安全注入方式
android·运维·服务器·安全·pos机·edc·刷卡
键来大师2 小时前
Android16 RK3576 系统清理缓存
android·缓存·framework·rk3588·android15
私人珍藏库2 小时前
[吾爱大神原创工具] PPT演讲倒计时工具 高级专业版 v2.0
android·app·工具·ppt·辅助
2501_916007473 小时前
没有 Mac,如何在 Windows 上架 iOS 应用?一套可落地的工程方案
android·macos·ios·小程序·uni-app·iphone·webview
正经教主3 小时前
【Trae+AI】和Trae学习搭建App_2.2.2:第4章·安卓APP调用Express后端实:2:网络请求工具封装(OkHttp3)
android·学习·app·express
2501_915106323 小时前
uni-app 上架 iOS 的完整实践,从跨端开发到稳定提交的工程路径
android·ios·小程序·uni-app·cocoa·iphone·webview
hnlgzb3 小时前
好像kotlin class和kotlin file都可以是activity?
android·开发语言·kotlin
一笑的小酒馆11 小时前
Android CameraX适配Android15
android
hnlgzb12 小时前
安卓app开发,如何快速上手kotlin和compose的开发?
android·开发语言·kotlin