fix: 修复BufferQueue abandoned导致进程崩溃重启

- cleanupVirtualDisplayOnly()释放顺序修正:先释放VirtualDisplay(消费者)再关闭ImageReader(生产者),防止Surface的BufferQueue被abandon后VirtualDisplay仍持有引用继续dequeueBuffer
- 所有资源释放路径统一先置null再释放,防止其他线程在释放过程中继续使用
- reinitializeVirtualDisplayForAndroid15()释放顺序同步修正
- setupMediaProjectionResources()中Surface无效时的释放顺序修正
- refreshSurfaceForAndroid15()移除危险的surface.release()直接调用,改为完整重建ImageReader+VirtualDisplay
- reinitializeImageReaderForAndroid15()先释放VirtualDisplay再重建ImageReader,重建后创建新VirtualDisplay关联
- 清理重复的catch代码块
This commit is contained in:
wdvipa
2026-02-15 01:10:53 +08:00
parent e93286cf31
commit 0c516f7307

View File

@@ -1244,10 +1244,13 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
val createdSurface = imageReader?.surface
if (createdSurface == null || !createdSurface.isValid) {
Log.e(TAG, "VirtualDisplay创建后Surface无效(null=${createdSurface == null}, isValid=${createdSurface?.isValid}), 清理资源")
virtualDisplay?.release()
// 释放顺序先VirtualDisplay再ImageReader
val failVd = virtualDisplay
val failIr = imageReader
virtualDisplay = null
imageReader?.close()
imageReader = null
try { failVd?.release() } catch (_: Exception) {}
try { failIr?.close() } catch (_: Exception) {}
return
}
@@ -2260,18 +2263,35 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
/**
* 只清理VirtualDisplay和ImageReader保留MediaProjection权限
*
* 释放顺序至关重要必须先释放VirtualDisplay消费者再关闭ImageReader生产者
* 如果先关闭ImageReader其内部Surface的BufferQueue会被abandon
* 但VirtualDisplay仍持有该Surface引用并继续dequeueBuffer
* 导致大量"BufferQueue has been abandoned"错误日志洪泛,最终进程崩溃
*/
private fun cleanupVirtualDisplayOnly() {
try {
// 清理VirtualDisplay
virtualDisplay?.release()
// 第一步保存引用并立即置null防止其他线程继续使用
val vd = virtualDisplay
val ir = imageReader
virtualDisplay = null
// 清理ImageReader
imageReader?.close()
imageReader = null
// 安全清理缓存的Bitmap防止多线程并发导致native crash (SIGSEGV)
// 第二步先释放VirtualDisplay消费者断开与Surface的连接
try {
vd?.release()
} catch (e: Exception) {
Log.w(TAG, "释放VirtualDisplay异常: ${e.message}")
}
// 第三步再关闭ImageReader生产者此时Surface不再被引用
try {
ir?.close()
} catch (e: Exception) {
Log.w(TAG, "关闭ImageReader异常: ${e.message}")
}
// 第四步安全清理缓存的Bitmap
safeRecycleLastValidBitmap()
lastCaptureTime = 0L
@@ -2455,11 +2475,18 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
isVirtualDisplayRecreating = true
Log.i(TAG, "[VirtualDisplay重建] 开始重新创建(第${consecutiveRecreationCount}次),抑制权限保活检查")
// 清理当前的VirtualDisplay和ImageReader,完全重新创建
virtualDisplay?.release()
// 清理当前的VirtualDisplay和ImageReader
// 释放顺序先VirtualDisplay消费者再ImageReader生产者
val oldVd = virtualDisplay
val oldIr = imageReader
virtualDisplay = null
imageReader?.close()
imageReader = null
try { oldVd?.release() } catch (e: Exception) {
Log.w(TAG, "重建时释放VirtualDisplay异常: ${e.message}")
}
try { oldIr?.close() } catch (e: Exception) {
Log.w(TAG, "重建时关闭ImageReader异常: ${e.message}")
}
// 等待系统完全清理
Thread.sleep(500)
@@ -2495,10 +2522,12 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
val rebuiltSurface = imageReader?.surface
if (rebuiltSurface == null || !rebuiltSurface.isValid) {
Log.e(TAG, "Android 15 VirtualDisplay重建后Surface无效, 放弃")
virtualDisplay?.release()
val failVd = virtualDisplay
val failIr = imageReader
virtualDisplay = null
imageReader?.close()
imageReader = null
try { failVd?.release() } catch (_: Exception) {}
try { failIr?.close() } catch (_: Exception) {}
isVirtualDisplayRecreating = false
return false
}
@@ -2733,21 +2762,37 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
/**
* 为Android 15强制刷新Surface
* 通过完整重建ImageReader实现不直接release Surface会导致悬空引用
*/
private suspend fun refreshSurfaceForAndroid15() {
try {
imageReader?.let { reader ->
val surface = reader.surface
if (surface.isValid) {
Log.d(TAG, "Android 15强制刷新Surface")
// 通过重设Surface的方式强制刷新
surface.release()
delay(500)
// 重新创建ImageReader如果需要
if (isVirtualDisplayCreated()) {
createImageReader()
}
// 不能直接release ImageReader的Surface这会导致ImageReader持有悬空引用
// 正确做法先释放VirtualDisplay再重建ImageReader最后重建VirtualDisplay
val oldVd = virtualDisplay
val oldIr = imageReader
virtualDisplay = null
imageReader = null
try { oldVd?.release() } catch (_: Exception) {}
try { oldIr?.close() } catch (_: Exception) {}
delay(500)
// 重新创建ImageReader
createImageReader()
// 重新创建VirtualDisplay关联新的Surface
if (imageReader != null && mediaProjection != null) {
virtualDisplay = mediaProjection?.createVirtualDisplay(
"RemoteControlCaptureRefresh_${System.currentTimeMillis()}",
screenWidth, screenHeight,
service.resources.displayMetrics.densityDpi,
android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
imageReader?.surface, null, null
)
if (virtualDisplay != null) {
Log.i(TAG, "Android 15 Surface刷新完成VirtualDisplay已重建")
} else {
Log.w(TAG, "Android 15 Surface刷新后VirtualDisplay创建失败")
}
}
} catch (e: Exception) {
@@ -2757,12 +2802,17 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
/**
* 为Android 15重新初始化ImageReader
* 必须先释放VirtualDisplay再重建ImageReader防止Surface悬空引用
*/
private suspend fun reinitializeImageReaderForAndroid15() {
try {
Log.d(TAG, "Android 15重新初始化ImageReader")
// 释放现有ImageReader
// 释放VirtualDisplay消费者再释放ImageReader(生产者)
val oldVd = virtualDisplay
virtualDisplay = null
try { oldVd?.release() } catch (_: Exception) {}
releaseImageReader()
delay(1000)
@@ -2770,11 +2820,19 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
createImageReader()
delay(1000)
// 如果VirtualDisplay存在,重新关联Surface
virtualDisplay?.let { display ->
imageReader?.let { reader ->
display.surface = reader.surface
Log.d(TAG, "Android 15已重新关联ImageReader和VirtualDisplay")
// 重新创建VirtualDisplay关联新的ImageReader Surface
if (imageReader != null && mediaProjection != null) {
virtualDisplay = mediaProjection?.createVirtualDisplay(
"RemoteControlCaptureReinit_${System.currentTimeMillis()}",
screenWidth, screenHeight,
service.resources.displayMetrics.densityDpi,
android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
imageReader?.surface, null, null
)
if (virtualDisplay != null) {
Log.d(TAG, "Android 15已重建VirtualDisplay关联新ImageReader")
} else {
Log.w(TAG, "Android 15重建VirtualDisplay失败")
}
}