起因
尝试写一个Camera2的Demo,第一步有surface成功了,第二步打算用ImageReader接收数据以便可以处理,但在第38帧左右的时候,预览画面就会卡死,不再继续播放,但程序并没有崩溃。
原因
像这种效果不一致,但程序并没有崩溃的现象无法从日志上直接定位到在哪一行,也有可能第三方库在某些地方做了异常捕获会错误判断导致,检查日志,发现多了一行BufferQueue has been abandoned,有一个队列被销毁了,猜测可能是gc引起的。
错误代码
private val stateCallback: CameraDevice.StateCallback = object : CameraDevice.StateCallback() {
@SuppressLint("NewApi")
override fun onOpened(camera: CameraDevice) {
Log.d(test, "相机开启")
//获取请求体
val request = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
//生成ImageReader
val imageReader = ImageReader.newInstance(1920, 1048, ImageFormat.YUV_420_888, 2)
//imageReader添加监听器
val handlerThread = HandlerThread("test2")
handlerThread.start()
imageReader.setOnImageAvailableListener({ imageListener(it) }, Handler(handlerThread.looper))
//添加ImageReader
request.addTarget(imageReader.surface)
//开启通道并把请求发出去
val outputs = ArrayList<OutputConfiguration>()
outputs.add(OutputConfiguration(imageReader.surface))
camera.createCaptureSession(
SessionConfiguration(SESSION_REGULAR,
outputs,
MyApplication.getContext().mainExecutor,
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
//通道开启 发送请求
session.setRepeatingRequest(request.build(), null, null)
}
override fun onConfigureFailed(session: CameraCaptureSession) {}
})
)
}
override fun onDisconnected(camera: CameraDevice) {
Log.d(test, "相机断开")
}
override fun onError(camera: CameraDevice, error: Int) {
Log.d(test, "相机发生错误")
}
}
解决方法
猜测是gc引起的,那就不gc好了,学activity被内部类持有导致不能gc而内存泄漏,解决方案就是弄个变量持有就行了
解决代码
/**
* 这里有一个坑,如果不保持imageReader的引用,
* imageReader会被垃圾回收。这意味着imageReader
* 可能只接收到几十帧的数据就会被回收,一旦被回收,就会
* 引起通道出错,导致其他surface无法获取数据,实际表现
* 就是卡死,但由于surface跑在子线程,并不会造成程序的
* 崩溃
*/
private lateinit var imageReader: ImageReader
private val stateCallback: CameraDevice.StateCallback = object : CameraDevice.StateCallback() {
@SuppressLint("NewApi")
override fun onOpened(camera: CameraDevice) {
Log.d(test, "相机开启")
//获取请求体
val request = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
//生成ImageReader
val imageReader = ImageReader.newInstance(1920, 1048, ImageFormat.YUV_420_888, 2)
//imageReader添加监听器
val handlerThread = HandlerThread("test2")
handlerThread.start()
imageReader.setOnImageAvailableListener({ imageListener(it) }, Handler(handlerThread.looper))
//添加ImageReader
request.addTarget(imageReader.surface)
//开启通道并把请求发出去
val outputs = ArrayList<OutputConfiguration>()
outputs.add(OutputConfiguration(imageReader.surface))
camera.createCaptureSession(
SessionConfiguration(SESSION_REGULAR,
outputs,
MyApplication.getContext().mainExecutor,
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
//通道开启 发送请求
session.setRepeatingRequest(request.build(), null, null)
}
override fun onConfigureFailed(session: CameraCaptureSession) {
}
})
)
}
override fun onDisconnected(camera: CameraDevice) {
Log.d(test, "相机断开")
}
override fun onError(camera: CameraDevice, error: Int) {
Log.d(test, "相机发生错误")
}
}
最后
可能还有别的方案处理,暂时没时间解决,这个方案是有缺陷的,如果imageReader作为变量还有别的用处,也碰不到这种错误,如果是像我这种写Demo的,这里就凭空多出来一个变量,在代码里看着强迫症都犯了。