fix: 彻底修复BufferQueue abandoned持续报错

- cleanupVirtualDisplayOnly先setSurface(null)切断生产者再release
- createVirtualDisplay注册Callback监听onStopped事件
- 新增bufferQueueAbandoned volatile标志位,回调无延迟设置
- 采集循环双重检测:标志位+Surface.isValid
- Android 15重试createVirtualDisplay同步添加Callback
- cleanup时主动设置bufferQueueAbandoned=true
This commit is contained in:
wdvipa
2026-02-14 23:09:23 +08:00
parent 548c9a1f15
commit 3ba594aa9f

View File

@@ -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
// 关闭ImageReaderSurface随之销毁
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 15ImageReader重新创建完成")
// 重新创建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完全重新创建成功")