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

API 23 Camera2 后台录像及Activity预览实现咨询(初学者)

嘿,作为刚入坑Android的新手,你这个后台录像的需求其实挺常见的——既要在Activity里有相机预览,离开后还得让录像继续跑。既然已经把Service的架子搭起来了,MediaRecorder也初始化就绪设了Surface源,那接下来核心就是搞定Surface的传递与持有、Service的前台保活这两个关键点,我给你一步步拆解:

核心逻辑梳理

因为Service本身没有UI界面,而你用了MediaRecorder.VideoSource.SURFACE作为视频源,所以必须从有预览的Activity把Surface传递给Service,并且让Service长期持有这个Surface的引用,同时把Service设为前台服务,避免被系统后台回收。

步骤1:在设置Activity中创建可传递的Surface

推荐用TextureView来承载相机预览,它的SurfaceTexture可以灵活生成Surface,而且比传统SurfaceView更适配后台场景。示例代码如下:

class CameraSettingsActivity : AppCompatActivity(), TextureView.SurfaceTextureListener {
    private lateinit var textureView: TextureView
    private var surface: Surface? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_camera_settings)
        textureView = findViewById(R.id.texture_view)
        textureView.surfaceTextureListener = this

        // 提前申请相机、存储等危险权限(这里省略权限申请逻辑,记得要动态申请!)
    }

    override fun onSurfaceTextureAvailable(surfaceTexture: SurfaceTexture, width: Int, height: Int) {
        // 当TextureView的Surface可用时,生成Surface对象
        surface = Surface(surfaceTexture)
        // 把Surface传递给后台Service,并启动录像
        val intent = Intent(this, CameraRecordService::class.java).apply {
            putExtra("RECORD_SURFACE", surface)
            action = "ACTION_START_RECORD"
        }
        startService(intent)
    }

    override fun onSurfaceTextureSizeChanged(surfaceTexture: SurfaceTexture, width: Int, height: Int) {
        // 可选:根据预览尺寸调整MediaRecorder的分辨率
    }

    override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture): Boolean {
        // 这里不要立即释放Surface!因为Service还在使用它录像
        // 等Service停止时再统一释放资源
        return false
    }

    override fun onSurfaceTextureUpdated(surfaceTexture: SurfaceTexture) {
        // 预览帧更新,无需额外操作
    }
}
步骤2:在Service中接管Surface并管理录像

这里要注意:Android 8.0+后台服务容易被系统杀死,所以必须把Service设为前台服务,同时要妥善处理MediaRecorder的生命周期:

class CameraRecordService : Service() {
    private var recorder: MediaRecorder? = null
    private var recordSurface: Surface? = null
    private var isRecording = false

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        intent?.let {
            when (it.action) {
                "ACTION_START_RECORD" -> {
                    // 获取从Activity传递来的Surface
                    recordSurface = it.getParcelableExtra("RECORD_SURFACE")
                    startBackgroundRecording()
                }
                "ACTION_STOP_RECORD" -> {
                    stopRecording()
                }
            }
        }
        // 让Service被杀死后尝试重启(可选,根据你的需求调整)
        return START_STICKY
    }

    private fun startBackgroundRecording() {
        if (isRecording || recordSurface == null) return

        recorder?.apply {
            // 你已经初始化好MediaRecorder,这里只需要绑定Surface并启动
            setPreviewDisplay(recordSurface)
            prepare()
            start()
            isRecording = true

            // 启动前台服务,必须创建通知(Android 8.0+要求)
            val notification = NotificationCompat.Builder(this@CameraRecordService, "RECORD_CHANNEL")
                .setContentTitle("后台录像中")
                .setContentText("点击返回设置页面")
                .setSmallIcon(R.drawable.ic_recording)
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .build()
            startForeground(1001, notification)
        }
    }

    private fun stopRecording() {
        if (!isRecording) return

        recorder?.apply {
            stop()
            reset()
            isRecording = false
        }
        stopForeground(true)
        stopSelf()
    }

    override fun onDestroy() {
        super.onDestroy()
        // 最后统一释放所有资源
        recorder?.release()
        recordSurface?.release()
    }

    override fun onBind(intent: Intent): IBinder? {
        return null // 如果不需要Activity和Service绑定交互,返回null即可
    }
}
必做的关键配置与注意事项
  • 权限申请:必须申请CAMERA、RECORD_AUDIO(如果需要录声音)、WRITE_EXTERNAL_STORAGE(Android 13+用READ_MEDIA_VIDEO/WRITE_MEDIA_VIDEO)这些危险权限,一定要做动态权限申请,不然会直接崩溃。
  • 通知通道创建:Android 8.0+必须提前创建前台服务的通知通道,建议在Application的onCreate方法中初始化:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel = NotificationChannel(
        "RECORD_CHANNEL",
        "后台录像通知",
        NotificationManager.IMPORTANCE_LOW
    )
    val notificationManager = getSystemService(NotificationManager::class.java)
    notificationManager.createNotificationChannel(channel)
}
  • 相机资源冲突:如果你的应用其他地方要使用相机,一定要确保Service停止录像并释放相机资源后,再让其他组件调用相机,不然会抛出相机被占用的异常。
  • 后台限制:Android 12+对后台相机有更严格的限制,如果你的应用需要长期后台录像,可能需要申请ACCESS_BACKGROUND_LOCATION或者其他特殊权限,或者引导用户把应用加入电池优化白名单。

内容的提问来源于stack exchange,提问作者Häkka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:14:30