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
相关产品推荐
相关产品推荐

