MVVM架构中MediaCodec及MediaCodec.Callback的合理放置位置咨询
MediaCodec 及 MediaCodec.Callback 在 MVVM 架构中的合理放置方案
核心结论
MediaCodec 及其 MediaCodec.Callback 应放在 Model 层,或封装为独立的底层服务类,ViewModel 仅负责协调 Model 与 View 的交互,不处理具体编解码逻辑。
各层职责与放置逻辑
MVVM 架构的核心是分层解耦,各层职责明确:
- View 层:仅负责 UI 渲染、用户交互,绝不触碰底层编解码操作。MediaCodec 初始化/销毁成本高,且 View 生命周期不稳定,放在这里会导致资源浪费、状态丢失。
- ViewModel 层:作为中间协调者,接收 View 的指令、调用 Model 方法,将处理结果转换为 View 可直接使用的数据(如 LiveData/Flow)。ViewModel 需保持轻量,不应耦合系统底层 API(MediaCodec 属于此类),否则会因 UI 销毁导致编解码中断。
- Model 层:负责所有与数据、底层操作相关的逻辑,包括媒体编解码的初始化、配置、回调处理。这完全符合 Model 层封装业务核心逻辑的职责。
具体实现建议
封装独立的编解码管理类
创建如MediaCodecManager的类(归入 Model 层),将 MediaCodec 的初始化、配置、回调、资源释放等逻辑全部封装在此类中:class MediaCodecManager { private var mediaCodec: MediaCodec? = null private var dataCallback: ((ByteArray) -> Unit)? = null fun setDataCallback(callback: (ByteArray) -> Unit) { this.dataCallback = callback } fun startEncoding(format: MediaFormat) { mediaCodec = MediaCodec.createEncoderByType(format.getString(MediaFormat.KEY_MIME)!!) mediaCodec?.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE) mediaCodec?.setCallback(object : MediaCodec.Callback() { override fun onInputBufferAvailable(codec: MediaCodec, index: Int) { // 填充待编码数据至输入缓冲区 val buffer = codec.getInputBuffer(index) buffer?.let { // 写入待编码数据(如摄像头帧数据) codec.queueInputBuffer(index, 0, it.capacity(), System.nanoTime(), 0) } } override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, info: MediaCodec.BufferInfo) { // 读取编码后的数据并传递给上层 val buffer = codec.getOutputBuffer(index) buffer?.let { val data = ByteArray(info.size) it.get(data) dataCallback?.invoke(data) } codec.releaseOutputBuffer(index, false) } override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) { // 处理编解码错误,可通过回调通知上层 } override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) { // 处理格式变更,通知上层 } }) mediaCodec?.start() } fun stopEncoding() { mediaCodec?.stop() mediaCodec?.release() mediaCodec = null } }ViewModel 中协调交互
ViewModel 依赖MediaCodecManager,调用其方法并监听数据回调,将结果转换为 View 可观察的数据流:class VideoEncodeViewModel : ViewModel() { private val mediaCodecManager = MediaCodecManager() val encodedDataFlow = MutableSharedFlow<ByteArray>() init { mediaCodecManager.setDataCallback { data -> viewModelScope.launch { encodedDataFlow.emit(data) } } } fun startEncoding() { val videoFormat = MediaFormat.createVideoFormat("video/avc", 1920, 1080) videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, 10_000_000) videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30) videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1) mediaCodecManager.startEncoding(videoFormat) } override fun onCleared() { super.onCleared() mediaCodecManager.stopEncoding() } }View 层仅做UI渲染
在 Activity/Fragment 中观察 ViewModel 的数据流,更新 UI 或处理数据:class VideoEncodeFragment : Fragment() { private val viewModel: VideoEncodeViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) lifecycleScope.launch { viewModel.encodedDataFlow.collect { data -> // 处理编码后的数据(如写入文件、推流) } } // 用户触发编码开始 startBtn.setOnClickListener { viewModel.startEncoding() } } }
内容的提问来源于stack exchange,提问作者user12653758
相关产品推荐
相关产品推荐

