news 2026/10/23 13:16:12

Android后台拍照实现与避坑:从Camera2到前台服务与画中画

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android后台拍照实现与避坑:从Camera2到前台服务与画中画

简介:针对Android平台在应用后台执行拍照的需求,这份资源面向移动端开发者和系统集成人员,覆盖服务(Service)、权限动态申请、Camera与Camera2 API调用、图片压缩存储、WakeLock防休眠及通知反馈等关键环节,适合需要实现静默抓拍、监控记录或后台图像采集的场景。压缩包共862个文件,约5.32MB,以png界面资源与xml配置布局为主,同时包含java源码、gradle构建脚本、class编译产物及debug版APK,可直接导入工程查看完整实现与运行效果。资源已获得783人学习下载,对于正在研究Android后台相机调用和系统兼容性处理的开发者,可参考其中的权限管理方式、相机会话构建逻辑以及图片处理流程,快速迁移到自身项目中。

1. 后台拍照的第一课:Android 的相机不是想开就开,它默认封锁看不见的调用者

很多人一听“后台拍照”,第一反应是把 openCamera 塞进一个后台 Service,再等回调取照片。实际跑一遍就会发现:回调要么不触发,要么直接抛 IllegalStateException,要么返回一张纯黑图。从 Android 9(API 28)开始,系统默认拒绝后台进程访问摄像头和麦克风,这已经不只是“可能要适配”的问题,而是平台的安全边界,普通应用基本绕不过去。

真正能落地的“后台拍照”,其实分三种:应用还显示在屏幕上,把拍照动作放到非 UI 线程去做;应用退到后台,但通过前台服务或画中画保住相机连接;以及纯粹的进程级后台,在目标版本上基本不可行。这篇文章就按这三条线展开,讲清每条的适用范围、最小代码和踩坑点。适合做亲子记录、会议留证、宠物监控这类用户明确知道相机在工作的场景;如果你的目标是让对方完全不知道被拍,那不是技术问题,建议直接放弃。

2. “后台”在 Android 里有三层含义:线程、服务、进程状态,分别决定你能不能动相机

2.1 第一层:“后台线程”不等于“后台应用”,线程池里拍照是合法常规操作

很多新手把“后台拍照”直接理解为“开一个线程去拍照”。这个理解有一半是对的:拍照本身确实要放到工作线程,否则相机回调、图像编码、写文件这些操作会卡主线程,导致掉帧甚至 ANR。但线程只是执行载体,不决定 Android 是否允许你访问相机,相机访问的判定标准是“应用界面是否对用户可见”。

先看一个最基础的 HandlerThread 准备代码:

private val cameraThread = HandlerThread("camera-capture-thread").apply { start() } private val cameraHandler = Handler(cameraThread.looper)

这段代码的作用是创建一个独立的 Looper 线程,后面 openCamera、createCaptureSession、capture 三个关键调用的回调都会跑到这个线程上。HandlerThread的 name 参数只影响调试日志,建议写清楚用途,比如camera-capture-thread,否则 dumpsys 排查线程时满屏都是 thread-1、thread-2,很难定位问题。

这里需要纠正一个常见误用:不要在主线程里直接调manager.openCamera。相机服务是个独占资源,打开、配置、出帧都有延迟,主线程只要等一次 open 回调就可能触发 StrictMode 警告,连续拍照时几乎必然卡 UI。正确做法是像上面的代码一样,先把 HandlerThread 跑起来,所有相机回调都投递到这个线程上,主线程只做按钮点击和状态提示。

但要注意,这条路径成立的前提是 Activity 或 Fragment 仍然可见。也就是说,用户正看着你的页面,你把拍照放到了后台线程,这个叫“后台线程拍照”,系统完全允许。搜索“Android后台拍照功能”时,大部分人其实要的是这个:不让界面卡顿、能快速连拍、能后台保存图片。

2.2 第二层:startService 不等于可以自由拍照,后台服务受多重启动限制约束

再往上走一层,“后台”变成了 Service。很多人写代码时是这么想的:App 退到桌面,Service 还在跑,于是 Service 里持续拍照,形成一个“后台自动拍摄”的效果。这个想法在 Android 8 之前勉强能做,Android 8 引入后台执行限制,Android 9 又单独把相机和麦克风列出来,后台 Service 直接自动打开相机的空窗口被基本关死。

服务能不能持有相机,主要看服务类型和用户可见性,可以拉一个简化对照表:

服务形态是否需要常驻通知相机能否可用典型限制
普通 startService 后台服务否Android 9 后基本不可用进程随时可能被系统回收
前台服务,声明 camera 类型是可持有相机通知必须存在,Android 14 起强制声明类型
绑定服务看宿主进程宿主可见即可用,宿主被杀则失效生命周期跟着 Activity 走
JobScheduler / WorkManager 任务否不适合拍照任务执行时机由系统决定,无法保证开相机时机

这里做个重要提醒:市面上确实有一些 App 打着后台拍照的名义,诱导用户开启无障碍服务或设备管理员权限。无障碍服务不是相机权限的合法旁路,Android 对无障碍服务抓取屏幕内容有额外限制,而且这类应用在应用商店审核阶段基本都会被驳回。别往这个方向走,合规路径只有前台服务加可见通知,或者让应用界面保持可见。

2.3 第三层:进程级后台是回收场,进程被杀后再“拍照”只是一个回调幻觉

最后一层是进程级后台。当用户退出界面后,Activity 走到 onStop,系统会根据 OOM 优先级把应用进程标记为 cached 或 empty,内存紧张时优先回收。就算你开了 Service,也只是把优先级从 cached 提升到 foreground service,不代表系统会永远保留你的进程。

这段代码展示了进程可能被回收的直接后果:

// 很多人会这么写,但它在进程被回收后等于空操作 object PhotoStore { lateinit var pendingPhoto: ByteArray } fun onCaptureCompleted(bytes: ByteArray) { PhotoStore.pendingPhoto = bytes // 如果此时进程被杀,数据直接丢失,后续"保存照片"的逻辑根本不会执行 }

在后台任务里,Java 对象常驻内存不等于数据安全。进程一旦被回收,所有内存态变量都没了,再等到下次进程重建,那个所谓的“延迟保存”其实什么都没干。正确的做法是:拍照完成后立刻写磁盘,或者用 WorkManager 把待处理的图片文件路径交给系统调度,而不是把字节数组放在内存里等待某个不存在的时机。

理解这三层之后,再回头看需求就清晰了。如果你的产品场景是“用户在页面里按一下快门,相机异步干活”,这是第 2.1 节的事。如果你的产品场景是“用户按 Home 键退到桌面,应用继续记录一段画面”,那必须走前台服务或画中画,同时接受通知栏常驻。如果你的产品场景是“用户熄屏睡了,手机放在床头自动拍”,现代 Android 的常规 API 不支持也不应该支持,尽早改需求。

3. 应用还可见时把“后台拍照”跑通:Camera2 异步拍摄的最小闭环

3.1 先申请权限,再决定用 Camera2 还是 CameraX

实现之前,权限是绕不开的第一步。Camera 权限属于危险权限,Android 6.0 之后必须在运行时申请。如果还要录制音频或视频,麦克风权限也得一起申请,否则带声音的拍摄会失败。

Manifest 里的声明如下:

<uses-feature android:name="android.hardware.camera.any" android:required="true" /> <uses-permission android:name="android.permission.CAMERA" /> <!-- Android 14+ 如果要用前台服务跑相机,必须加这行 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />

运行时的权限申请逻辑不在这里展开,重点说后面的选型。CameraX 适合大多数常规业务,因为它封装了生命周期、旋转和线程切换,几分钟就能跑通拍照流程;但它对底层控制做了较多收口,如果你想精确控制对焦、曝光时间或连续捕获帧,Camera2 更直接。网上大量“后台拍照”的教程基于 Camera2,原因不是 Camera2 更高级,而是它能明确看到TEMPLATE_STILL_CAPTURE、CONTROL_AF_TRIGGER这类参数,方便排查问题。

我一般会建议:业务里只需要拍一张清晰的图,选 CameraX;需要自己控制快门、对焦、曝光,或者要高频连拍,选 Camera2。下面的代码按 Camera2 写,因为你要在一线排查黑屏、模糊、曝光错误,绕不开这些底层参数。

3.2 用 CameraManager + HandlerThread 的最小拍照样本

先创建一个接收 JPEG 数据的 ImageReader。它的作用是把相机输出的帧交给你的回调,你再从回调里拿字节写文件。

val imageReader = ImageReader.newInstance( targetWidth, targetHeight, ImageFormat.JPEG, 2 ) imageReader.setOnImageAvailableListener({ reader -> val image = reader.acquireLatestImage() if (image != null) { val buffer = image.planes[0].buffer val bytes = ByteArray(buffer.remaining()) buffer.get(bytes) savePhotoToDisk(bytes) // 写文件务必放在子线程,不能丢到主线程 image.close() } }, cameraHandler)

newInstance的四个参数分别是宽、高、像素格式、缓冲数量。maxImages设为 2 通常够用,设为 1 容易出现生产者消费者互相等待的问题,设为 4 以上会占更多内存。JPEG 编码后图片不会太大,2 个缓冲对大多数 1080P 场景绰绰有余。

acquireLatestImage会丢弃中间帧、只取最新一帧,适合“只要当前画面”的拍照场景;如果你要做连拍不漏帧,换成acquireNextImage,但每一帧都得及时 close,否则缓冲很快耗尽。

接着打开相机并触发一次单帧拍照:

val manager = getSystemService(Context.CAMERA_SERVICE) as CameraManager val cameraId = getBackCameraId(manager) // 通过摄像头朝向选择,别写死 "0" @SuppressLint("MissingPermission") manager.openCamera(cameraId, object : CameraDevice.StateCallback() { override fun onOpened(camera: CameraDevice) { val request = camera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply { addTarget(imageReader.surface) set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON) set(CaptureRequest.JPEG_QUALITY, 90.toByte()) } camera.createCaptureSession( listOf(imageReader.surface), object : CameraCaptureSession.StateCallback() { override fun onConfigured(session: CameraCaptureSession) { session.capture(request.build(), null, cameraHandler) } override fun onConfigureFailed(session: CameraCaptureSession) { // 常见原因是 surface 已被释放,或相机已被其他应用占用 camera.close() } }, cameraHandler ) currentCamera = camera } override fun onDisconnected(camera: CameraDevice) { camera.close() } override fun onError(camera: CameraDevice, error: Int) { camera.close() // error 为 ERROR_CAMERA_IN_USE 时,需要提示用户关闭其他相机应用 } }, cameraHandler)

这段代码有几个参数值得记住:

参数推荐值踩坑说明
CONTROL_AF_MODECONTROL_AF_MODE_CONTINUOUS_PICTURE适合静物和大多数场景;拍近距离文档建议改成 MACRO 模式
CONTROL_AE_MODECONTROL_AE_MODE_ON不要关闭自动曝光,否则容易全黑或全白
JPEG_QUALITY85~9590 是画质和体积的平衡点,追求高速保存可以降到 80
TEMPLATE_STILL_CAPTURE单帧拍照模板如果要连拍,换成 TEMPLATE_PREVIEW 或复用同一个 capture request 多次 capture

onConfigured里只调一次session.capture,这时候系统会完成对焦、曝光、出帧、JPEG 编码,最后把照片送到 ImageReader。流程结束后,养成好习惯:session.close()和camera.close()成对出现。不 close 的后果是相机服务一直认为资源被占,再次打开时直接报CAMERA_IN_USE。

3.3 把它改成定时拍/连拍需要的三个关键参数

应用还可见时,定时拍照是常见需求,比如每隔 5 秒拍一张记录实验过程。很多人会重新打开相机再拍,实际上不需要。Camera2 的 capture session 支持在一次配置后多次捕获,你只需要调整三个部分。

第一,把TEMPLATE_STILL_CAPTURE创建的 request 保存到一个变量里,不要每次拍照都重新 createCaptureRequest。第二,对焦和曝光参数保持不变,特别是CONTROL_AF_MODE_CONTINUOUS_PICTURE和CONTROL_AE_MODE_ON,这样连续拍照时不会每张重新对焦导致节奏变慢。第三,在定时器回调里直接调session.capture(savedRequest, null, cameraHandler),每次 capture 都是一次新拍照。

下面是一个简单定时拍照骨架:

private val timerHandler = Handler(Looper.getMainLooper()) private val photoRunnable = object : Runnable { override fun run() { if (::session.isInitialized) { session.capture(stillRequest, object : CameraCaptureSession.CaptureCallback() { override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) { // 走到这里代表一张照片已经生成,可以更新 UI 计数 } }, cameraHandler) } timerHandler.postDelayed(this, 5000L) } }

注意这段代码里的定时任务用了主线程的 Handler,但实际的 capture 工作仍然投递给cameraHandler,所以 UI 线程只负责计时和刷新状态。postDelayed的周期不建议小于 800ms,因为相机的 AE/AF 收敛加上 JPEG 编码需要时间,拍得太快会出现一部分照片曝光还没来得及稳定。

还有一个容易翻车的小细节:定时器停止时,一定要timerHandler.removeCallbacks(photoRunnable),否则页面关闭后任务还在跑,相机已经被 close,下一次 capture 会抛异常。这个异常在后台线程里甚至不会直接导致崩溃,但会在 logcat 里刷屏,而且用户看到的表面现象是“有时候拍了、有时候没拍”。

4. 应用真正退到后台时,只剩前台服务和画中画两条配合路,且都要用户看得见

4.1 前台服务 + camera 类型:通知常驻是绕不开的代价

当用户按 Home 键退出页面,Activity 会走到 onStop。这时候如果不做任何处理,相机连接会被系统收走。唯一能继续持有相机的方法,是把拍摄动作挪到前台服务里,并且声明 camera 服务类型。

Android 14 之后,前台服务必须显式声明类型,Manifest 里这样写:

<service android:name=".CameraForegroundService" android:foregroundServiceType="camera" android:exported="false" />

启动服务时,也要把类型传进去:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { startForeground( NOTIFICATION_ID, createCameraNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA ) } else { startForeground(NOTIFICATION_ID, createCameraNotification()) }

createCameraNotification()里必须提供一个可见通知,内容是“正在使用相机”之类的说明。用户下拉通知栏就能看到,这个通知不能取消,除非服务停止。很多产品经理会认为这个通知影响体验,要求隐藏通知,但隐藏通知就意味着应用失去了合法持有相机的资格,这不是参数调整的问题,而是平台规则。

服务内持有相机的代码和上一章的 Camera2 流程几乎一样,只是把权限判断、回调逻辑从 Activity 挪到 Service 里。要特别注意的是,Service 本身可能被系统重启,onDestroy里必须释放相机资源。很多“后台拍照应用退出后还在拍”的诡异现象,多半是进程被杀后系统重新拉起 Service,但相机没有重新初始化,导致拍照代码在跑、实际没有画面。

4.2 画中画模式里维持相机预览和拍照的代码骨架

画中画是另一个合规路径。它的原理是 Activity 仍然保留一个小窗口在屏幕上,用户看得到应用存在,所以相机访问不被视为后台。

Android 8.0 以上可以这样进入画中画:

override fun onUserLeaveHint() { super.onUserLeaveHint() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val params = PictureInPictureParams.Builder() .setAspectRatio(Rational(previewWidth, previewHeight)) .build() enterPictureInPictureMode(params) } }

onUserLeaveHint在用户按 Home 键时触发,这时候调用enterPictureInPictureMode会让 Activity 缩成小窗,而不是直接退到桌面。setAspectRatio要传宽高比,这里用的是预览画面的比例,不能传翻转后的值,否则小窗会被拉伸。

画中画模式下,Activity 会走到 onStop,但它仍然在屏幕上可见,所以相机连接可以继续维持。你需要在onPictureInPictureModeChanged里做状态感知:

override fun onPictureInPictureModeChanged( isInPictureInPictureMode: Boolean, newConfig: Configuration ) { super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig) // 进入画中画后可以继续拍照,但建议降低频率以节省处理资源 isPipActive = isInPictureInPictureMode }

我见过一个实际项目:应用做会议记录,用户按 Home 后进入画中画小窗,相机会每隔 10 秒抓一帧当前画面,然后把图片写到本地,备注里带上时间戳。这个方案既能满足“退到后台还能拍”的需求,又保证了用户始终看得到相机预览,审核和合规都没有问题。

4.3 说清边界:熄屏之后所有路径都会断,别过度设计

画中画和前台服务都依赖一个前提:屏幕或通知栏让用户知道相机还在工作。如果用户关了屏幕,应用想继续在纯后台拍摄,这条路径在现代 Android 上是断的。

有些方案会尝试用 WakeLock 保持系统不熄屏。WakeLock 能让屏幕保持常亮,但代价是功耗飙升,而且长时间常亮会导致系统温度过高,相机服务也会因为温度限制关闭摄像头。这个方案只能用在“行驶记录仪”这类充电中、固定支架、用户知情且有明确时长限制的硬件场景里,不适合普通手机上的常驻功能。

更稳妥的替代方案是:熄屏时停止拍照,等到用户亮屏或设备检测到运动事件后再恢复。运动检测用 SensorManager 的加速度传感器,耗电比相机低一个数量级,这是产品设计上更聪明的取舍。

5. 避坑:后台拍照开发中最常见的 5 个翻车现场与排查手段

5.1 权限明明给了,openCamera 还是失败或回调不到

现象:运行时权限对话框点了允许,代码里也用checkSelfPermission确认了,但openCamera要么抛异常,要么onOpened一直不被回调。

原因:多数情况是应用当前处于后台或不可见状态。Android 9 之后,后台应用调用 openCamera 不会得到正常回调,而是直接失败。还有一种可能是其他应用占用了相机,比如系统相机、扫码应用正在运行,相机资源是全局独占的,不存在多应用共享。

解决:在 openCamera 前先判断应用可见性,可以用Lifecycle的状态判断 Activity 是否处于 STARTED 以上;同时注册CameraManager.AvailabilityCallback监听相机占用变化:

manager.registerAvailabilityCallback(object : CameraManager.AvailabilityCallback() { override fun onCameraAvailable(cameraId: String) { // 相机释放后,这里会回调,此时再执行打开操作 } }, cameraHandler)

5.2 第一张照片糊成一片,第二张开始才清晰

现象:点击拍照后,第一张 JPEG 明显模糊,第二张、第三张正常。看起来像是“相机没准备好就拍了”。

原因:对焦还没收敛。CONTROL_AF_MODE_CONTINUOUS_PICTURE会让相机持续自动对焦,但相机从打开到完成首次对焦需要几百毫秒。第一次capture如果发生在对焦完成前,就会拿到失焦帧。

解决:在真正出片前,先发一个对焦触发请求,等CONTROL_AF_STATE变成FOCUSED_LOCKED后再拍:

val afRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply { set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START) } session.capture(afRequest.build(), object : CameraCaptureSession.CaptureCallback() { override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) { val afState = result.get(CaptureResult.CONTROL_AF_STATE) if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED) { // 对焦已完成,再执行真正的拍照 session.capture(stillRequest, null, cameraHandler) } } }, cameraHandler)

这里的CONTROL_AF_TRIGGER_START是一次性触发,不是持续设置。触发完成后,系统会自动进入锁定状态,锁定的时机就是判断CONTROL_AF_STATE_FOCUSED_LOCKED的标准。

5.3 暗光环境下照片全是噪点,甚至一片黑

现象:灯光正常的会议室里拍出来还能看,一到傍晚或室内暗角就出现大量噪点,亮度也明显偏低。

原因:相机自动曝光在高 ISO 下会牺牲画质,Camera2 默认策略倾向于保证亮度,导致噪点放大。另一个常见问题是 ImageReader 的宽高比与传感器不匹配,裁切后相当于用了更差的感光区域。

解决:在弱光场景下主动限制 ISO,并在预览和拍照之间切换时保持曝光平稳。一个可操作的做法是用CONTROL_AE_TARGET_FPS_RANGE把帧率降到 15fps,让每帧有更长曝光时间;同时把图片分辨率降到 1080P,减少图片处理压力和噪点放大。记住一个原则:暗光下想要画质,优先降低帧率而不是提高 ISO。

5.4 Android 9 以上后台打开相机直接抛 IllegalStateException

现象:整个流程在目标设备上偶尔崩溃,logcat 里能看到类似 “Camera is being used after Camera.release” 或后台访问相机的权限拒绝信息,且只在用户按 Home 键或锁屏后出现。

原因:应用退到后台但 Service 还尝试重新打开相机。Android 9 引入了后台相机访问限制,前台服务如果不带 camera 类型或不满足可见通知要求,同样会被拦。

解决:给 Service 正确声明 camera 类型,并在onTaskRemoved、onDestroy里无条件关闭相机。写代码时养成一个习惯:相机资源的释放不能只写在成功路径上,异常路径同样要执行 close。

5.5 部分机型连拍几分钟后手机发烫,照片输出速度越来越慢

现象:连续拍照 5 分钟后,照片保存间隔从 1 秒拉长到 3 秒,手机背部明显升温,部分设备直接弹出过热提示。

原因:高分辨率 JPEG 编码、ImageWriter 写文件、屏幕常亮三者叠加,处理器和相机模组同时发热。相机服务检测到温度过高时,会主动降低输出帧率或直接关闭摄像头。

解决:把保存文件从 JPEG 编码后写磁盘改成直接用共享内存映射,或者降低输出分辨率。更简单的方法是限制连续拍照时长,比如每拍 50 张强制休息 5 秒。手机散热条件有限,后台拍照这种场景更应该给用户一个“连续拍摄时长上限”的设置项,而不是无限制跑传感器。

6. 用 adb 把“后台”状态当黑盒验证,再用预热和降级策略把快门延迟压下去

6.1 用 adb 验证应用是否真的在后台

很多后台问题必须真机复现才查得出来,但人工按 Home 键太慢也不可重复。我一般直接用 adb 把应用切到后台,再观察相机连接状态。

# 显示当前栈顶 Activity,确认应用是否在前台 adb shell dumpsys activity activities | grep -E "topResumedActivity|ResumedActivity" # 把应用退到桌面,等价于用户按 Home 键 adb shell input keyevent KEYCODE_HOME # 查看指定包名的服务是否仍在前台服务列表里 adb shell dumpsys activity services 你的包名

执行完这三条命令后,看 logcat 里有没有相机设备的销毁日志。如果应用声称支持后台拍照,但 Home 键一按就出现相机 close 日志,说明代码里的前台服务或画中画并没有真正生效。这个过程看起来很笨,但它能直接区分“代码里写了后台支持”和“真实设备上确实还活着”。

6.2 快门延迟优化:预热、复用会话、降级参数

后台拍照的实际体验瓶颈不在后台化,而在快门延迟。openCamera 通常要 300 到 800 毫秒,createCaptureSession 又要 100 到 300 毫秒,用户按下快门时才开始真正出图,体验会很差。

我现在的习惯是:在用户进入拍照页面的那一刻就预热相机。提前 openCamera,提前创建 capture session,图片格式先不用 JPEG,而是先用 YUV 帧稳定 AE/AF。按下快门时,直接把单帧 JPEG capture 投递到已经配置好的 session 里。这样快门到文件落盘的时间可以压到 200 毫秒以内。

降级策略也很重要。如果设备已经有延迟,优先降低 JPEG 质量和分辨率,而不是降低拍照频率。一张 4000×3000 的 JPEG 编码可能耗时 300 毫秒,但降级到 1920×1080 后耗时可以减半,画面损失对大多数记录场景完全可接受。

最后说一个我踩过的教训:每次改完相机代码,不要只在目标测试机上验证,一定要拿一台低端机和一台折叠屏真机跑一遍后台切换。折叠屏的 hinge 状态切换、低端机的相机服务初始化速度,都会把原本隐藏的问题放大。现在的我写相机相关代码,永远先写 close 再写 open,永远先验证后台状态再谈新功能。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 21:20:05

Python模拟登陆学信网查询四六级成绩,告别准考证号丢失

简介&#xff1a;这份Android示例项目实现了模拟登录学信网并查询四六级成绩&#xff0c;适合正在学习网络编程与数据解析的Android开发者。项目完整展示了从构造登录请求、携带Cookie维持会话&#xff0c;到解析返回HTML并提取成绩信息的全过程&#xff0c;涉及HttpURLConnect…

作者头像 李华
网站建设 2026/10/22 22:49:33

桌面宠物游戏开发全流程:从技术选型到发售的实战拆解

1. 一款桌面宠物游戏从立项到发售的全流程拆解《Pixel Kitten Desktop》这个名字第一次出现在我视野里的时候&#xff0c;我正蹲在一个独立游戏开发群里看人聊天。有人甩了张截图&#xff0c;一只像素风的小猫趴在任务栏上打哈欠&#xff0c;底下配文“正式发售”。说实话&…

作者头像 李华
网站建设 2026/10/18 18:01:02

CEGUI 0.7.4输入法集成案例:从消息路由到候选词窗口

简介&#xff1a;针对CEGUI-0.7.4中文输入法场景的配套代码包&#xff0c;面向需要在游戏或工具界面中实现中文文本输入的开发者。由于完整SDK体量过大&#xff0c;作者仅整理输入法相关代码与资源&#xff0c;可在已有CEGUI-0.7.4环境&#xff08;需支持中文显示&#xff09;中…

作者头像 李华
网站建设 2026/10/22 12:24:43

GermOS嵌入式平台:为微生物实时监测设备打造统一调度系统

如果你和我一样&#xff0c;常年和菌落、培养皿、传感器、环境监测打交道&#xff0c;大概率碰到过同一个尴尬&#xff1a;设备买了好几台&#xff0c;每个厂家的数据格式都不一样&#xff0c;接口一个比一个难对接。去年我在调试一套微生物实时监测装置时&#xff0c;被底层软…

作者头像 李华
网站建设 2026/10/19 14:32:32

告别来回切窗口:Proxima CLI 12个命令让你在终端里指挥全部AI

【免费下载链接】Proxima Multi-AI MCP Server - Connect ChatGPT, Claude, Gemini & Perplexity to your coding tools without any API 项目地址&#xff1a; https://gitcode.com/gh_mirrors/proxima1/Proxima 点击查看 免费下载 Proxima 是一款 Multi-AI MCP Server&a…

作者头像 李华