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

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()
        }
    }
}
通用方案总结

不管是拍照还是录音,通用执行流程都是:

  1. 权限全量授权:申请并动态获取CAMERA、RECORD_AUDIO、FOREGROUND_SERVICE等权限,还要引导用户开启「忽略电池优化」,避免系统杀后台。
  2. 前台服务启动:Android 8.0+必须将Service设为前台服务,否则无法在后台调用敏感硬件。
  3. 唤醒锁保持运行:获取PARTIAL_WAKE_LOCK,确保锁屏下CPU持续运行。
  4. 无预览硬件调用:拍照用CameraX或虚拟SurfaceTexture,录音直接用MediaRecorder,无需界面支持。
  5. 及时释放资源:用完相机/麦克风后立即释放,唤醒锁也要及时关闭,避免不必要的耗电。
注意事项
  • Android 12+对前台服务类型做了严格限制,必须在清单文件中指定foregroundServiceType="camera|microphone",否则会触发权限报错。
  • 部分国产厂商(如小米、华为)有独立的后台管控机制,需要引导用户手动允许App的「后台运行权限」。
  • 锁屏下频繁调用硬件会增加设备耗电,建议仅在防丢触发条件(如收到远程指令)时执行,不要长期驻留后台。

内容的提问来源于stack exchange,提问作者Arslan khan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:46:05