From 3ba594aa9fa900b81a9b756fc39c057d82f3b41b Mon Sep 17 00:00:00 2001 From: wdvipa Date: Sat, 14 Feb 2026 23:09:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=BD=BB=E5=BA=95=E4=BF=AE=E5=A4=8DBuff?= =?UTF-8?q?erQueue=20abandoned=E6=8C=81=E7=BB=AD=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cleanupVirtualDisplayOnly先setSurface(null)切断生产者再release - createVirtualDisplay注册Callback监听onStopped事件 - 新增bufferQueueAbandoned volatile标志位,回调无延迟设置 - 采集循环双重检测:标志位+Surface.isValid - Android 15重试createVirtualDisplay同步添加Callback - cleanup时主动设置bufferQueueAbandoned=true --- .../hikoncont/manager/ScreenCaptureManager.kt | 51 +++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt index d8b2ef0..e4aaeb1 100644 --- a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt +++ b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt @@ -89,6 +89,10 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { private var virtualDisplayRebuildCount = 0 private val MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS = 3 // 最多重建3次,之后直接回退到无障碍截图 + // ✅ BufferQueue abandoned 标志:由VirtualDisplay回调或Surface检测设置,采集循环读取 + @Volatile + private var bufferQueueAbandoned = false + // 🔑 新增:AccessibilityService截图模式开关 private var useAccessibilityScreenshot = false @@ -405,15 +409,16 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { val MAX_BLACK_FRAMES_BEFORE_FALLBACK = 30 // 连续30个黑屏帧后切换到无障碍截图 while (isCapturing) { - // ✅ BufferQueue abandoned 检测:Surface失效时立即回退,避免大量错误日志 + // ✅ BufferQueue abandoned 检测:标志位 + Surface失效双重检查 + // bufferQueueAbandoned 由 VirtualDisplay.Callback.onStopped 设置,无延迟 val surfaceValid = try { - imageReader?.surface?.isValid == true + !bufferQueueAbandoned && imageReader?.surface?.isValid == true } catch (e: Exception) { Log.w(TAG, "⚠️ Surface有效性检查异常: ${e.message}") false } if (!surfaceValid) { - Log.w(TAG, "🚨 Surface已失效(BufferQueue abandoned),停止MediaProjection采集") + Log.w(TAG, "🚨 Surface已失效(BufferQueue abandoned=${bufferQueueAbandoned}),停止MediaProjection采集") cleanupVirtualDisplayOnly() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { Log.i(TAG, "🔄 回退到无障碍截图模式") @@ -1199,15 +1204,25 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.i(TAG, "ImageReader创建完成") - // 创建VirtualDisplay + // 创建VirtualDisplay,注册Callback监听停止事件 + val displayCallback = object : android.hardware.display.VirtualDisplay.Callback() { + override fun onStopped() { + Log.w(TAG, "🚨 VirtualDisplay.Callback.onStopped: 系统已停止VirtualDisplay") + bufferQueueAbandoned = true + } + } + virtualDisplay = mediaProjection?.createVirtualDisplay( "RemoteControlCapture", screenWidth, screenHeight, service.resources.displayMetrics.densityDpi, android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, - imageReader?.surface, null, null + imageReader?.surface, displayCallback, backgroundHandler ) + // 重置abandoned标志(新的VirtualDisplay创建成功) + bufferQueueAbandoned = false + if (virtualDisplay != null) { Log.i(TAG, "VirtualDisplay创建成功") @@ -2245,14 +2260,25 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { */ private fun cleanupVirtualDisplayOnly() { try { - // 清理VirtualDisplay + // ✅ 先切断VirtualDisplay与Surface的连接,阻止生产者继续dequeueBuffer + // 这是解决 "BufferQueue has been abandoned" 持续报错的关键 + try { + virtualDisplay?.surface = null + } catch (e: Exception) { + Log.w(TAG, "⚠️ 切断VirtualDisplay Surface连接失败: ${e.message}") + } + + // 释放VirtualDisplay virtualDisplay?.release() virtualDisplay = null - // 清理ImageReader + // 关闭ImageReader(Surface随之销毁) imageReader?.close() imageReader = null + // 标记abandoned状态 + bufferQueueAbandoned = true + // ✅ 安全清理缓存的Bitmap,防止多线程并发导致native crash (SIGSEGV) safeRecycleLastValidBitmap() lastCaptureTime = 0L @@ -2454,14 +2480,21 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.i(TAG, "🔧 Android 15:ImageReader重新创建完成") - // 重新创建VirtualDisplay + // 重新创建VirtualDisplay(带Callback监听停止事件) + val retryDisplayCallback = object : android.hardware.display.VirtualDisplay.Callback() { + override fun onStopped() { + Log.w(TAG, "🚨 Android 15重试VirtualDisplay.Callback.onStopped") + bufferQueueAbandoned = true + } + } virtualDisplay = mediaProjection?.createVirtualDisplay( "RemoteControlCaptureRetry_${System.currentTimeMillis()}", screenWidth, screenHeight, service.resources.displayMetrics.densityDpi, android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, - imageReader?.surface, null, null + imageReader?.surface, retryDisplayCallback, backgroundHandler ) + bufferQueueAbandoned = false if (virtualDisplay != null) { Log.i(TAG, "✅ Android 15 VirtualDisplay完全重新创建成功")