You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用CameraX录制视频时文件写入耗时过长(Pixel设备问题)

CameraX在Pixel设备录制后文件写入耗时过长的问题解决

问题原因

你的计时器实现是在**主线程协程(Dispatchers.Main.immediate)**中通过循环delay来计时,最终调用captureVideo()停止录制。在Pixel设备上,这种方式会和CameraX的录制收尾逻辑(文件写入)产生主线程调度冲突:

  • 主线程的消息队列被协程的挂起/恢复操作持续占用,导致CameraX处理VideoRecordEvent.Finalize事件时(包含关键的文件写入收尾步骤)无法及时获取主线程资源,从而出现长达10秒的写入延迟。
  • 手动调用curRecording.stop()的时机由主线程协程控制,和CameraX内部的录制状态同步逻辑不匹配,进一步加剧了文件写入阻塞。

解决方案

改用CameraX内置的录制时长限制功能,让CameraX内部自动处理录制停止逻辑,避免主线程调度冲突;计时器仅用于UI更新,不再触发停止操作。

修改代码示例

  1. 更新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
                    }
                }
            }
        }
}
  1. 修改计时器方法,仅用于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 13:22:14