【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状态,防止非法操作持续;
  • 极简暴露 :对外仅暴露状态转换规则,底层实现完全封装。
相关推荐
xiangpanf30 分钟前
Laravel 10.x重磅升级:五大核心特性解析
android
robotx3 小时前
安卓线程相关
android
消失的旧时光-19434 小时前
Android 面试高频:JSON 文件、大数据存储与断电安全(从原理到工程实践)
android·面试·json
dalancon5 小时前
VSYNC 信号流程分析 (Android 14)
android
dalancon5 小时前
VSYNC 信号完整流程2
android
dalancon5 小时前
SurfaceFlinger 上帧后 releaseBuffer 完整流程分析
android
用户69371750013846 小时前
不卷AI速度,我卷自己的从容——北京程序员手记
android·前端·人工智能
程序员Android6 小时前
Android 刷新一帧流程trace拆解
android
墨狂之逸才7 小时前
解决 Android/Gradle 编译报错:Comparison method violates its general contract!
android
阿明的小蝴蝶8 小时前
记一次Gradle环境的编译问题与解决
android·前端·gradle