Android防丢App开发:锁屏下通过Service实现拍照录音的代码方案
嘿,我来帮你搞定这个锁屏下用Service实现拍照、录音的需求!既然你已经能触发Service了,那接下来核心就是搞定锁屏状态下的硬件权限、系统休眠限制,还有不同Android版本的适配坑。下面给你一步步拆解方案和代码:
核心前提:权限与系统限制处理
在动手写代码前,这些权限和配置必须先搞定,不然锁屏下根本调用不了硬件:
- 权限申请:在
AndroidManifest.xml里加上必要权限,还要注意Android 6.0+的动态申请:<!-- 基础硬件权限 --> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 后台运行与唤醒相关 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <!-- 声明Service,指定前台服务类型 --> <service android:name=".LockScreenCaptureService" android:foregroundServiceType="camera|microphone" /> - 前台服务强制适配:Android 8.0+之后,后台Service极易被系统杀死,尤其是调用相机/麦克风这类敏感硬件时,必须把你的Service设为前台服务,还要指定对应的
foregroundServiceType。 - 唤醒锁:锁屏后系统会进入休眠状态,CPU可能暂停工作,所以需要申请
PARTIAL_WAKE_LOCK来保持CPU运行,确保Service能正常执行硬件调用逻辑。
锁屏下拍照实现(Service内)
推荐用CameraX(Android Jetpack组件,适配性更强),旧的Camera API已经被官方弃用,这里给你两种方案参考:
方案1:CameraX无预览拍照(推荐)
CameraX不需要依赖Activity的预览界面,完美适配Service场景:
class LockScreenCaptureService : Service() { private lateinit var wakeLock: PowerManager.WakeLock private var cameraProvider: ProcessCameraProvider? = null private var imageCapture: ImageCapture? = null override fun onCreate() { super.onCreate() // 申请唤醒锁,最多保持10分钟,用完记得释放 val powerManager = getSystemService(POWER_SERVICE) as PowerManager wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "CaptureService:WakeLock") wakeLock.acquire(10*60*1000L) // 启动前台服务,避免被系统杀死 val notification = NotificationCompat.Builder(this, "CaptureChannel") .setContentTitle("防丢服务运行中") .setSmallIcon(R.drawable.ic_notification) .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE) .build() startForeground(1, notification) // 初始化CameraX val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener({ cameraProvider = cameraProviderFuture.get() setupCamera() takePhoto() }, ContextCompat.getMainExecutor(this)) } private fun setupCamera() { val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() try { cameraProvider?.unbindAll() cameraProvider?.bindToLifecycle(this, cameraSelector, imageCapture) } catch (e: Exception) { e.printStackTrace() } } private fun takePhoto() { val photoFile = File(externalMediaDirs.first(), "lockscreen_photo_${System.currentTimeMillis()}.jpg") val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build() imageCapture?.takePicture( outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { Log.d("CaptureService", "照片已保存:${photoFile.absolutePath}") // 执行后续逻辑(如上传、本地存储) releaseResources() } override fun onError(exception: ImageCaptureException) { exception.printStackTrace() releaseResources() } } ) } private fun releaseResources() { cameraProvider?.unbindAll() if (wakeLock.isHeld) wakeLock.release() stopForeground(STOP_FOREGROUND_REMOVE) stopSelf() } override fun onBind(intent: Intent): IBinder? = null override fun onDestroy() { super.onDestroy() releaseResources() } }
方案2:旧Camera API适配(兼容Android 9及以下)
如果需要兼容老版本系统,可以用旧Camera API,但要创建虚拟SurfaceTexture来绕过预览界面的要求:
// Java简化示例 private Camera camera; private void takePhotoWithOldCamera() { camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); try { // 创建虚拟SurfaceTexture,避免无预览界面报错 SurfaceTexture dummySurface = new SurfaceTexture(0); camera.setPreviewTexture(dummySurface); camera.startPreview(); // 触发拍照 camera.takePicture(null, null, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { // 将字节数组保存为图片文件 File photoFile = new File(getExternalFilesDir(null), "old_camera_photo.jpg"); try (FileOutputStream fos = new FileOutputStream(photoFile)) { fos.write(data); } catch (IOException e) { e.printStackTrace(); } // 释放相机资源 camera.stopPreview(); camera.release(); } }); } catch (IOException e) { e.printStackTrace(); } }
锁屏下录音实现(Service内)
录音逻辑相对简单,用MediaRecorder即可,注意后台运行的权限约束:
private fun startRecording() { val recorder = MediaRecorder().apply { setAudioSource(MediaRecorder.AudioSource.MIC) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.AAC) setOutputFile(File(externalMediaDirs.first(), "lockscreen_recording_${System.currentTimeMillis()}.m4a").absolutePath) try { prepare() start() // 示例:录制10秒后自动停止 Handler(Looper.getMainLooper()).postDelayed({ stop() release() releaseResources() }, 10*1000L) } catch (e: IOException) { e.printStackTrace() releaseResources() } } }
通用方案总结
不管是拍照还是录音,通用执行流程都是:
- 权限全量授权:申请并动态获取CAMERA、RECORD_AUDIO、FOREGROUND_SERVICE等权限,还要引导用户开启「忽略电池优化」,避免系统杀后台。
- 前台服务启动:Android 8.0+必须将Service设为前台服务,否则无法在后台调用敏感硬件。
- 唤醒锁保持运行:获取
PARTIAL_WAKE_LOCK,确保锁屏下CPU持续运行。 - 无预览硬件调用:拍照用CameraX或虚拟SurfaceTexture,录音直接用MediaRecorder,无需界面支持。
- 及时释放资源:用完相机/麦克风后立即释放,唤醒锁也要及时关闭,避免不必要的耗电。
注意事项
- Android 12+对前台服务类型做了严格限制,必须在清单文件中指定
foregroundServiceType="camera|microphone",否则会触发权限报错。 - 部分国产厂商(如小米、华为)有独立的后台管控机制,需要引导用户手动允许App的「后台运行权限」。
- 锁屏下频繁调用硬件会增加设备耗电,建议仅在防丢触发条件(如收到远程指令)时执行,不要长期驻留后台。
内容的提问来源于stack exchange,提问作者Arslan khan
相关产品推荐
相关产品推荐

