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:
@@ -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完全重新创建成功")
|
||||
|
||||
Reference in New Issue
Block a user