From 0c516f7307fa9f348c7b5dc13b9390e72b8d6f8e Mon Sep 17 00:00:00 2001 From: wdvipa Date: Sun, 15 Feb 2026 01:10:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DBufferQueue=20abandone?= =?UTF-8?q?d=E5=AF=BC=E8=87=B4=E8=BF=9B=E7=A8=8B=E5=B4=A9=E6=BA=83?= =?UTF-8?q?=E9=87=8D=E5=90=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cleanupVirtualDisplayOnly()释放顺序修正:先释放VirtualDisplay(消费者)再关闭ImageReader(生产者),防止Surface的BufferQueue被abandon后VirtualDisplay仍持有引用继续dequeueBuffer - 所有资源释放路径统一先置null再释放,防止其他线程在释放过程中继续使用 - reinitializeVirtualDisplayForAndroid15()释放顺序同步修正 - setupMediaProjectionResources()中Surface无效时的释放顺序修正 - refreshSurfaceForAndroid15()移除危险的surface.release()直接调用,改为完整重建ImageReader+VirtualDisplay - reinitializeImageReaderForAndroid15()先释放VirtualDisplay再重建ImageReader,重建后创建新VirtualDisplay关联 - 清理重复的catch代码块 --- .../hikoncont/manager/ScreenCaptureManager.kt | 120 +++++++++++++----- 1 file changed, 89 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt index 2124065..b0606dc 100644 --- a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt +++ b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt @@ -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失败") } }