
MediaRecorder状态机
MediaRecorder的状态机是Android多媒体框架中分层管控+状态校验+原子性转换的核心设计,其实现贯穿Java层、JNI/Native层、MediaServer服务层,通过"每层维护独立状态变量+操作前严格校验+跨层状态同步"保证状态转换的合法性,避免非法操作导致的资源泄漏或功能异常。
以下从实现层面拆解核心机制:
核心设计
MediaRecorder的状态机并非单一模块实现,而是三层联动的架构,每层维护自己的状态变量,且上层状态转换必须同步到下层,最终以MediaServer服务端的状态为"最终权威"。
| 层级 | 状态存储载体 | 核心作用 |
|---|---|---|
| Java层 | 私有枚举变量mState |
对外暴露API的第一层校验,屏蔽底层细节,给开发者反馈IllegalStateException |
| JNI/Native层 | MediaRecorder.cpp的mCurrentState |
桥接Java层与MediaServer,同步Java层状态,二次校验避免非法跨进程调用 |
| MediaServer层 | StagefrightRecorder的mState |
最终状态管控,所有音视频采集/编码操作的实际状态校验,防止跨进程异常 |
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())的行为依赖当前状态,状态不合法则直接拒绝; - 状态转换由框架内部管控,开发者只需遵循公开的状态转换规则,无需关心底层细节。
关键注意点
- 状态校验的优先级:Java层校验是"快速失败",Native层和MediaServer层校验是"最终保障",即使绕过Java层反射修改状态,MediaServer也会拒绝非法操作;
- 多线程安全 :
mState的修改和读取都加锁,避免多线程下的状态错乱(如主线程调用start(),子线程调用stop()); - 错误状态的不可逆性 :进入ERROR状态后,必须调用
reset()恢复到IDLE,否则所有操作都会失败。
总结
MediaRecorder的状态机实现核心是:
- 分层管控:Java层(对外)、Native层(桥接)、MediaServer层(实际执行)各维护状态,层层校验;
- 原子转换:状态修改加锁,避免多线程/跨进程下的不一致;
- 异常回滚:错误时自动转入ERROR状态,防止非法操作持续;
- 极简暴露 :对外仅暴露状态转换规则,底层实现完全封装。
