使用CameraX录制视频时文件写入耗时过长(Pixel设备问题)
CameraX在Pixel设备录制后文件写入耗时过长的问题解决
问题原因
你的计时器实现是在**主线程协程(Dispatchers.Main.immediate)**中通过循环delay来计时,最终调用captureVideo()停止录制。在Pixel设备上,这种方式会和CameraX的录制收尾逻辑(文件写入)产生主线程调度冲突:
- 主线程的消息队列被协程的挂起/恢复操作持续占用,导致CameraX处理
VideoRecordEvent.Finalize事件时(包含关键的文件写入收尾步骤)无法及时获取主线程资源,从而出现长达10秒的写入延迟。 - 手动调用
curRecording.stop()的时机由主线程协程控制,和CameraX内部的录制状态同步逻辑不匹配,进一步加剧了文件写入阻塞。
解决方案
改用CameraX内置的录制时长限制功能,让CameraX内部自动处理录制停止逻辑,避免主线程调度冲突;计时器仅用于UI更新,不再触发停止操作。
修改代码示例
- 更新
captureVideo()方法,添加时长限制并调整事件处理:
private fun captureVideo() { val videoCapture = this.videoCapture ?: return val curRecording = recording if (curRecording != null) { curRecording.stop() recording = null return } outputFile = File(getOutputDirectory(), replaceSpaceIntoUnderscore(currentReturnKey) + ".mp4") outputFile?.takeIf { it.exists() }?.delete()?.let { Log.i("isdelet", it.toString()) } val mediaStoreOutputOptions = FileOutputOptions.Builder(outputFile!!).build() isRecordingStart = true recording = videoCapture.output .prepareRecording(this, mediaStoreOutputOptions) .apply { // 让CameraX自动控制录制时长(3秒) setMaxDurationMillis(TimeUnit.SECONDS.toMillis(3)) // 启用音频(权限检查保留) if (PermissionChecker.checkSelfPermission( this@PCVideoCameraActivity, Manifest.permission.RECORD_AUDIO ) == PermissionChecker.PERMISSION_GRANTED ) { withAudioEnabled() } } .start(recordingExecutor) { recordEvent -> when (recordEvent) { is VideoRecordEvent.Start -> { runOnUiThread { binding.ivAnimation.gone() } // 仅启动计时器更新UI,不触发停止 startRecordingTimerForUI() } is VideoRecordEvent.Finalize -> { countdownJob?.cancel() recording = null runOnUiThread { showExoPLayer() } if (!recordEvent.hasError() || recordEvent.error == VideoRecordEvent.Finalize.ERROR_DURATION_LIMIT_REACHED) { Log.i("FinalizeTest", "NoError$outputFile") } else { Log.i("Error", "${recordEvent.cause?.message}") recording?.close() recording = null } } } } }
- 修改计时器方法,仅用于UI更新:
private fun startRecordingTimerForUI() { isTimerRunning = true countdownJob = lifecycleScope.launch(Dispatchers.Main) { val durationMillis = TimeUnit.SECONDS.toMillis(3) remainingTimeMillis = durationMillis while (remainingTimeMillis > 0 && isTimerRunning) { updateTimerText(remainingTimeMillis) delay(1000) remainingTimeMillis -= 1000 } isTimerRunning = false // 移除原有的captureVideo()调用,不再手动停止录制 } }
额外注意事项
- 保持
recordingExecutor为独立的单线程(Executors.newSingleThreadExecutor()),避免和主线程共享资源。 - 尽量依赖CameraX的原生API处理录制生命周期,减少手动干预状态同步的逻辑,避免设备特定的兼容性问题。
内容的提问来源于stack exchange,提问作者Ali Jawad Hussain Gorayya
相关产品推荐
相关产品推荐

