常见CameraX资源泄漏模式概述
CameraX的资源泄漏,说白了就是该释放的没释放。我排查过的线上问题里,90% 都逃不出下面这六种模式。今天咱们把它们掰开揉碎了讲清楚,后面实战的时候你心里就有谱了。
1. 生命周期泄漏 —— 最常见的坑
Activity 或 Fragment 销毁了,CameraX 还在偷偷干活。这种情况最普遍,也最好修。
// 错误示例:onDestroy 里啥也没干
@Override
protected void onDestroy() {
// ❌ 忘了调用以下任何一行
// cameraProvider.unbindAll();
// cameraProvider.shutdown();
// lifecycle.removeObserver(myLifecycleObserver);
}
我记得有个社交App的扫码页面,每次打开内存涨 10MB+,关闭后一点不降。查了一圈,就是 onDestroy 里漏了 unbindAll。
2. Image 泄漏 —— 吃内存最快
ImageAnalysis 的 Analyzer 里,每一帧拿到的 ImageProxy 都必须 close。否则的话…… 1080p 分辨率下一帧就几 MB,30fps 跑个几秒,你想想看,几百 MB 就没了。
// 错误示例:ImageProxy 没 close
imageAnalysis.setAnalyzer(executor, imageProxy -> {
byte[] buffer = ImageUtil.toByteArray(imageProxy);
// 处理 buffer ...
// ❌ 忘了调用 imageProxy.close()
});
// ✅ 正确做法:finally 里确保关闭
imageAnalysis.setAnalyzer(executor, imageProxy -> {
try {
byte[] buffer = ImageUtil.toByteArray(imageProxy);
// 处理 buffer ...
} finally {
imageProxy.close(); // 必须 close!
}
});
我在项目中遇到过电商 AR 试穿功能,上线后 OOM 频报。一查,Analyzer 里处理完图像直接 return 了,ImageProxy 根本没 close。修复后内存直接降了 70%。
3. Provider 泄漏 —— 单例不背锅
ProcessCameraProvider.getInstance() 返回的是进程级单例,这本身没问题。但很多人反复绑定却不解绑,导致内部监听器、Session 对象越积越多。
// 错误示例:重复绑定不考虑解绑
ProcessCameraProvider.getInstance(context, executor, provider -> {
provider.bindToLifecycle(lifecycleOwner, cameraSelector, imageAnalysis);
// 下次调用又 bind 一次,旧的监听器还在
});
我个人习惯是在每次 bind 之前先调一次 unbindAll(),确保干净的绑定状态。
4. Listener 泄漏 —— 看不见的引用链
CameraX 提供了很多回调:CameraStateCallback、CameraProviderCallback、VideoCapture.OnVideoSavedCallback…… 这些如果注册了没取消,会死死拽住你的 Activity 或 Fragment。
// 错误示例:注册了但没取消
camera.registerCameraStateCallback(executor, stateCallback);
// ❌ 在适当位置忘了取消
// camera.unregisterCameraStateCallback(stateCallback);
我曾经在一个直播项目里,切换摄像头越来越卡,dumpsys meminfo 一看,CameraStateCallback 注册了 20 多个。就是因为每次切换摄像头 new 了新的 callback,旧的没解绑。
5. Session 泄漏 —— 用例残留
当你重复调用 bindToLifecycle 或者 start/stop 相机时,旧的 UseCase Session 如果没有被正确关闭,就会一直留在内存里。这种泄漏比较隐蔽,通常伴随着 Camera 设备的异常占用。
// 错误示例:反复 start/stop 不清理
camera.start(); // 内部创建新 Session
camera.stop(); // 如果不彻底销毁 Session,旧 Session 会残留
camera.start(); // 又创建一个新 Session
这种情况在快速切换前后摄像头的场景下特别容易触发。我个人建议在 start 之前强制 unbindAll,确保没有旧 Session 残留。
6. Executor 泄漏 —— 线程在后台"长生不老"
CameraX 允许传入自定义 Executor。很多人传了一个 ExecutorService 进去,用完却忘了 shutdown。结果就是线程池一直在后台挂着,GC 也回收不了。
// 错误示例:自定义 Executor 没关闭
ExecutorService cameraExecutor = Executors.newSingleThreadExecutor();
cameraProvider.bindToLifecycle(lifecycleOwner, selector, useCase, cameraExecutor);
// ❌ 在 onDestroy 中忘了调用 cameraExecutor.shutdown();
我个人觉得这种泄漏虽然不会立刻导致 OOM,但线程泄漏积累起来,App 会越来越卡顿。特别是低端机上,多几个僵尸线程,CPU 开销就上去了。
六大模式速查表
这六种模式,我把它们叫做 CameraX 泄漏的"六边形战士"。大部分线上问题,你照着这个表去排查,基本都能找到根因。下一回遇到内存暴涨或者相机卡死,别慌,先看看是哪个模式在搞鬼。