fix: 回退到d4f27bb基础上做最小改动
- 回退之前所有激进修改(setSurface/Callback/Surface检查) - 仅保留: 重建阈值从10降到5, 重建次数上限3次 - 添加CancellationException正确传播 - 不修改cleanupVirtualDisplayOnly清理逻辑 - 不添加Surface有效性检查和VirtualDisplay Callback
This commit is contained in:
@@ -83,15 +83,11 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
|
|
||||||
// ✅ 图像获取失败计数
|
// ✅ 图像获取失败计数
|
||||||
private var consecutiveImageFailures = 0
|
private var consecutiveImageFailures = 0
|
||||||
private val maxImageFailuresBeforeRecreation = 5 // 连续5次失败即考虑重新创建(从10降低,加速回退)
|
private val maxImageFailuresBeforeRecreation = 5 // 连续5次失败即重建(加速回退)
|
||||||
|
|
||||||
// ✅ VirtualDisplay重建次数限制,防止无限重建循环
|
// ✅ VirtualDisplay重建次数限制
|
||||||
private var virtualDisplayRebuildCount = 0
|
private var virtualDisplayRebuildCount = 0
|
||||||
private val MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS = 3 // 最多重建3次,之后直接回退到无障碍截图
|
private val MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS = 3
|
||||||
|
|
||||||
// ✅ BufferQueue abandoned 标志:由VirtualDisplay回调或Surface检测设置,采集循环读取
|
|
||||||
@Volatile
|
|
||||||
private var bufferQueueAbandoned = false
|
|
||||||
|
|
||||||
// 🔑 新增:AccessibilityService截图模式开关
|
// 🔑 新增:AccessibilityService截图模式开关
|
||||||
private var useAccessibilityScreenshot = false
|
private var useAccessibilityScreenshot = false
|
||||||
@@ -368,7 +364,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
captureScope.launch {
|
captureScope.launch {
|
||||||
// 先确保 MediaProjection 可用
|
// 先确保 MediaProjection 可用
|
||||||
if (!ensureMediaProjection()) {
|
if (!ensureMediaProjection()) {
|
||||||
Log.w(TAG, "⚠️ MediaProjection 不可用,回退到无障碍截图")
|
Log.e(TAG, "❌ MediaProjection 不可用,回退到无障碍截图")
|
||||||
// 回退到无障碍截图作为兜底
|
// 回退到无障碍截图作为兜底
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
enableAccessibilityScreenshotMode()
|
enableAccessibilityScreenshotMode()
|
||||||
@@ -385,7 +381,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (virtualDisplay == null) {
|
if (virtualDisplay == null) {
|
||||||
Log.w(TAG, "⚠️ VirtualDisplay 创建失败,回退到无障碍截图")
|
Log.e(TAG, "❌ VirtualDisplay 创建失败,回退到无障碍截图")
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
enableAccessibilityScreenshotMode()
|
enableAccessibilityScreenshotMode()
|
||||||
startAccessibilityScreenCapture()
|
startAccessibilityScreenCapture()
|
||||||
@@ -398,8 +394,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
Log.i(TAG, "✅ MediaProjection VirtualDisplay 就绪,开始连续帧采集循环")
|
Log.i(TAG, "✅ MediaProjection VirtualDisplay 就绪,开始连续帧采集循环")
|
||||||
|
|
||||||
var consecutiveFailures = 0
|
var consecutiveFailures = 0
|
||||||
virtualDisplayRebuildCount = 0 // 重置重建计数
|
virtualDisplayRebuildCount = 0
|
||||||
bufferQueueAbandoned = false // 重置abandoned标志,确保采集循环正常启动
|
|
||||||
|
|
||||||
// ✅ 最小有效帧大小阈值:正常480×854 JPEG即使最低质量也>5KB
|
// ✅ 最小有效帧大小阈值:正常480×854 JPEG即使最低质量也>5KB
|
||||||
// 低于此值的帧几乎肯定是黑屏/空白帧(VirtualDisplay未刷新)
|
// 低于此值的帧几乎肯定是黑屏/空白帧(VirtualDisplay未刷新)
|
||||||
@@ -410,26 +405,6 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
val MAX_BLACK_FRAMES_BEFORE_FALLBACK = 30 // 连续30个黑屏帧后切换到无障碍截图
|
val MAX_BLACK_FRAMES_BEFORE_FALLBACK = 30 // 连续30个黑屏帧后切换到无障碍截图
|
||||||
|
|
||||||
while (isCapturing) {
|
while (isCapturing) {
|
||||||
// ✅ BufferQueue abandoned 检测:标志位 + Surface失效双重检查
|
|
||||||
// bufferQueueAbandoned 由 VirtualDisplay.Callback.onStopped 设置,无延迟
|
|
||||||
val surfaceValid = try {
|
|
||||||
!bufferQueueAbandoned && imageReader?.surface?.isValid == true
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, "⚠️ Surface有效性检查异常: ${e.message}")
|
|
||||||
false
|
|
||||||
}
|
|
||||||
if (!surfaceValid) {
|
|
||||||
Log.w(TAG, "🚨 Surface已失效(BufferQueue abandoned=${bufferQueueAbandoned}),停止MediaProjection采集")
|
|
||||||
cleanupVirtualDisplayOnly()
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
Log.i(TAG, "🔄 回退到无障碍截图模式")
|
|
||||||
enableAccessibilityScreenshotMode()
|
|
||||||
startAccessibilityScreenCapture()
|
|
||||||
} else {
|
|
||||||
startFallbackCapture()
|
|
||||||
}
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
val image = imageReader?.acquireLatestImage()
|
val image = imageReader?.acquireLatestImage()
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
@@ -507,14 +482,12 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
// 连续失败过多,尝试重建 VirtualDisplay
|
// 连续失败过多,尝试重建 VirtualDisplay
|
||||||
if (consecutiveFailures >= maxImageFailuresBeforeRecreation) {
|
if (consecutiveFailures >= maxImageFailuresBeforeRecreation) {
|
||||||
virtualDisplayRebuildCount++
|
virtualDisplayRebuildCount++
|
||||||
Log.w(TAG, "⚠️ 连续 ${consecutiveFailures} 次无法获取有效帧,尝试重建 VirtualDisplay (第${virtualDisplayRebuildCount}/${MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS}次)")
|
Log.w(TAG, "⚠️ 连续 ${consecutiveFailures} 次无法获取有效帧,重建 VirtualDisplay (${virtualDisplayRebuildCount}/${MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS})")
|
||||||
|
|
||||||
// ✅ 重建次数超限,直接回退到无障碍截图
|
|
||||||
if (virtualDisplayRebuildCount > MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS) {
|
if (virtualDisplayRebuildCount > MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS) {
|
||||||
Log.w(TAG, "🚨 VirtualDisplay重建次数已达上限(${MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS}),MediaProjection在此设备不可靠")
|
Log.w(TAG, "🚨 重建次数已达上限,回退到无障碍截图")
|
||||||
cleanupVirtualDisplayOnly()
|
cleanupVirtualDisplayOnly()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
Log.i(TAG, "🔄 回退到无障碍截图模式")
|
|
||||||
enableAccessibilityScreenshotMode()
|
enableAccessibilityScreenshotMode()
|
||||||
startAccessibilityScreenCapture()
|
startAccessibilityScreenCapture()
|
||||||
} else {
|
} else {
|
||||||
@@ -528,16 +501,8 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
setupMediaProjectionResources()
|
setupMediaProjectionResources()
|
||||||
consecutiveFailures = 0
|
consecutiveFailures = 0
|
||||||
|
|
||||||
// ✅ 重建后验证Surface有效性
|
if (virtualDisplay == null) {
|
||||||
val rebuildSurfaceValid = try {
|
Log.w(TAG, "⚠️ VirtualDisplay 重建失败,回退到无障碍截图")
|
||||||
imageReader?.surface?.isValid == true
|
|
||||||
} catch (e: Exception) {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (virtualDisplay == null || !rebuildSurfaceValid) {
|
|
||||||
Log.w(TAG, "⚠️ VirtualDisplay 重建失败或Surface无效,回退到无障碍截图")
|
|
||||||
cleanupVirtualDisplayOnly()
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
enableAccessibilityScreenshotMode()
|
enableAccessibilityScreenshotMode()
|
||||||
startAccessibilityScreenCapture()
|
startAccessibilityScreenCapture()
|
||||||
@@ -551,7 +516,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
delay(1000L / dynamicFps)
|
delay(1000L / dynamicFps)
|
||||||
|
|
||||||
} catch (e: CancellationException) {
|
} catch (e: CancellationException) {
|
||||||
throw e // 协程正常取消,向上传播
|
throw e
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ MediaProjection 帧采集异常: ${e.message}")
|
Log.w(TAG, "⚠️ MediaProjection 帧采集异常: ${e.message}")
|
||||||
consecutiveFailures++
|
consecutiveFailures++
|
||||||
@@ -591,7 +556,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.w(TAG, "⚠️ 无法获取 MediaProjection,权限可能未授予")
|
Log.e(TAG, "❌ 无法获取 MediaProjection,权限可能未授予")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,11 +628,11 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: CancellationException) {
|
} catch (e: CancellationException) {
|
||||||
throw e // 协程正常取消,向上传播
|
throw e
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 屏幕捕获失败: ${e.message}")
|
Log.w(TAG, "⚠️ 屏幕捕获失败: ${e.message}")
|
||||||
consecutiveFailures++
|
consecutiveFailures++
|
||||||
delay(100) // ✅ 出错时进一步缩短间隔,保持流畅度(从200ms改为100ms)
|
delay(100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -836,7 +801,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
Log.w(TAG, "⚠️ ScreenshotResult中的HardwareBuffer为null")
|
Log.w(TAG, "⚠️ ScreenshotResult中的HardwareBuffer为null")
|
||||||
// 🚨 Android 11+特殊处理:记录详细错误信息
|
// 🚨 Android 11+特殊处理:记录详细错误信息
|
||||||
if (Build.VERSION.SDK_INT >= 30) {
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
Log.w(TAG, "📱 Android 11+设备:HardwareBuffer为null,可能是权限问题")
|
Log.e(TAG, "📱 Android 11+设备:HardwareBuffer为null,可能是权限问题")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -847,7 +812,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
Log.e(TAG, "处理截图结果失败", e)
|
Log.e(TAG, "处理截图结果失败", e)
|
||||||
// 🚨 Android 11+特殊处理:记录详细异常信息
|
// 🚨 Android 11+特殊处理:记录详细异常信息
|
||||||
if (Build.VERSION.SDK_INT >= 30) {
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
Log.d(TAG, "📱 Android 11+设备:处理截图结果时发生异常: ${e.message}")
|
Log.e(TAG, "📱 Android 11+设备:处理截图结果时发生异常", e)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
latch.countDown()
|
latch.countDown()
|
||||||
@@ -855,7 +820,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failureErrorCode: Int) {
|
override fun onFailure(failureErrorCode: Int) {
|
||||||
Log.w(TAG, "无障碍服务截图失败,错误码: $failureErrorCode")
|
Log.e(TAG, "无障碍服务截图失败,错误码: $failureErrorCode")
|
||||||
errorCode = failureErrorCode
|
errorCode = failureErrorCode
|
||||||
|
|
||||||
// 🚨 Android 11+特殊处理:详细分析错误码
|
// 🚨 Android 11+特殊处理:详细分析错误码
|
||||||
@@ -867,7 +832,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS -> "无障碍权限不足"
|
android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS -> "无障碍权限不足"
|
||||||
else -> "未知错误($failureErrorCode)"
|
else -> "未知错误($failureErrorCode)"
|
||||||
}
|
}
|
||||||
Log.w(TAG, "📱 Android 11+设备:截图失败详情 - $errorMessage")
|
Log.e(TAG, "📱 Android 11+设备:截图失败详情 - $errorMessage")
|
||||||
}
|
}
|
||||||
|
|
||||||
latch.countDown()
|
latch.countDown()
|
||||||
@@ -878,7 +843,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
Log.e(TAG, "调用takeScreenshot失败", e)
|
Log.e(TAG, "调用takeScreenshot失败", e)
|
||||||
// 🚨 Android 11+特殊处理:记录调用失败详情
|
// 🚨 Android 11+特殊处理:记录调用失败详情
|
||||||
if (Build.VERSION.SDK_INT >= 30) {
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
Log.d(TAG, "📱 Android 11+设备:调用takeScreenshot时发生异常: ${e.message}")
|
Log.e(TAG, "📱 Android 11+设备:调用takeScreenshot时发生异常", e)
|
||||||
}
|
}
|
||||||
latch.countDown()
|
latch.countDown()
|
||||||
}
|
}
|
||||||
@@ -923,7 +888,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
Log.e(TAG, "无障碍服务截图异常", e)
|
Log.e(TAG, "无障碍服务截图异常", e)
|
||||||
// 🚨 Android 11+特殊处理:异常时返回null让主循环处理
|
// 🚨 Android 11+特殊处理:异常时返回null让主循环处理
|
||||||
if (Build.VERSION.SDK_INT >= 30) {
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
Log.d(TAG, "📱 Android 11+设备:无障碍截图异常,返回null让主循环处理: ${e.message}")
|
Log.e(TAG, "📱 Android 11+设备:无障碍截图异常,返回null让主循环处理", e)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
// 出现异常时回退到MediaProjection
|
// 出现异常时回退到MediaProjection
|
||||||
@@ -968,13 +933,13 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
Log.i(TAG, "✅ Android 15静默恢复成功,继续屏幕捕获")
|
Log.i(TAG, "✅ Android 15静默恢复成功,继续屏幕捕获")
|
||||||
} else {
|
} else {
|
||||||
// 静默恢复失败,可能确实需要重新申请权限
|
// 静默恢复失败,可能确实需要重新申请权限
|
||||||
Log.w(TAG, "⚠️ Android 15静默恢复失败,MediaProjection权限确实丢失")
|
Log.e(TAG, "❌ Android 15静默恢复失败,MediaProjection权限确实丢失")
|
||||||
triggerPermissionRecovery()
|
triggerPermissionRecovery()
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 其他版本的处理逻辑
|
// 其他版本的处理逻辑
|
||||||
Log.w(TAG, "⚠️ MediaProjection权限丢失,触发自动权限恢复")
|
Log.e(TAG, "❌ MediaProjection权限丢失,触发自动权限恢复")
|
||||||
triggerPermissionRecovery()
|
triggerPermissionRecovery()
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -1205,41 +1170,18 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
|
|
||||||
Log.i(TAG, "ImageReader创建完成")
|
Log.i(TAG, "ImageReader创建完成")
|
||||||
|
|
||||||
// 创建VirtualDisplay,注册Callback监听停止事件
|
// 创建VirtualDisplay
|
||||||
val displayCallback = object : android.hardware.display.VirtualDisplay.Callback() {
|
|
||||||
override fun onStopped() {
|
|
||||||
Log.w(TAG, "🚨 VirtualDisplay.Callback.onStopped: 系统已停止VirtualDisplay")
|
|
||||||
bufferQueueAbandoned = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtualDisplay = mediaProjection?.createVirtualDisplay(
|
virtualDisplay = mediaProjection?.createVirtualDisplay(
|
||||||
"RemoteControlCapture",
|
"RemoteControlCapture",
|
||||||
screenWidth, screenHeight,
|
screenWidth, screenHeight,
|
||||||
service.resources.displayMetrics.densityDpi,
|
service.resources.displayMetrics.densityDpi,
|
||||||
android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
|
android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
|
||||||
imageReader?.surface, displayCallback, backgroundHandler
|
imageReader?.surface, null, null
|
||||||
)
|
)
|
||||||
|
|
||||||
// 重置abandoned标志(新的VirtualDisplay创建成功)
|
|
||||||
bufferQueueAbandoned = false
|
|
||||||
|
|
||||||
if (virtualDisplay != null) {
|
if (virtualDisplay != null) {
|
||||||
Log.i(TAG, "VirtualDisplay创建成功")
|
Log.i(TAG, "VirtualDisplay创建成功")
|
||||||
|
|
||||||
// ✅ 创建后立即验证Surface有效性,防止BufferQueue abandoned
|
|
||||||
val surfaceValid = try {
|
|
||||||
imageReader?.surface?.isValid == true
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, "⚠️ Surface有效性验证异常: ${e.message}")
|
|
||||||
false
|
|
||||||
}
|
|
||||||
if (!surfaceValid) {
|
|
||||||
Log.e(TAG, "🚨 VirtualDisplay创建后Surface已失效,清理资源")
|
|
||||||
cleanupVirtualDisplayOnly()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Android 15:VirtualDisplay创建成功后触发权限申请处理
|
// ✅ Android 15:VirtualDisplay创建成功后触发权限申请处理
|
||||||
if (Build.VERSION.SDK_INT >= 35) {
|
if (Build.VERSION.SDK_INT >= 35) {
|
||||||
Log.i(TAG, "🔧 Android 15:VirtualDisplay创建成功,触发权限申请处理")
|
Log.i(TAG, "🔧 Android 15:VirtualDisplay创建成功,触发权限申请处理")
|
||||||
@@ -1460,7 +1402,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
Log.i(TAG, "🔒 已标记Android 15 session为已使用")
|
Log.i(TAG, "🔒 已标记Android 15 session为已使用")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 标记Android 15 session失败", e)
|
Log.e(TAG, "标记Android 15 session失败", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1483,7 +1425,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 通知MediaProjection权限丢失失败", e)
|
Log.e(TAG, "通知MediaProjection权限丢失失败", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1506,7 +1448,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
Log.i(TAG, "屏幕捕获已暂停,权限保留,状态已持久化")
|
Log.i(TAG, "屏幕捕获已暂停,权限保留,状态已持久化")
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 暂停屏幕捕获失败", e)
|
Log.e(TAG, "暂停屏幕捕获失败", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1529,7 +1471,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
startCapture()
|
startCapture()
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 恢复屏幕捕获失败", e)
|
Log.e(TAG, "恢复屏幕捕获失败", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1665,7 +1607,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
Log.d(TAG, "🎯 智能压缩完成: 原始${bitmap.width}x${bitmap.height} -> 输出${compressedData.size} bytes (质量$quality, 尝试${attempts}次)")
|
Log.d(TAG, "🎯 智能压缩完成: 原始${bitmap.width}x${bitmap.height} -> 输出${compressedData.size} bytes (质量$quality, 尝试${attempts}次)")
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 智能压缩失败,使用基础压缩", e)
|
Log.e(TAG, "❌ 智能压缩失败,使用基础压缩", e)
|
||||||
// 如果智能压缩失败,使用基础压缩
|
// 如果智能压缩失败,使用基础压缩
|
||||||
outputStream.reset()
|
outputStream.reset()
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, dynamicQuality, outputStream)
|
bitmap.compress(Bitmap.CompressFormat.JPEG, dynamicQuality, outputStream)
|
||||||
@@ -1741,7 +1683,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
droppedFrameCount++
|
droppedFrameCount++
|
||||||
}
|
}
|
||||||
if (!screenDataQueue.offer(frameData)) {
|
if (!screenDataQueue.offer(frameData)) {
|
||||||
Log.w(TAG, "⚠️ 队列清理后仍无法添加新帧,跳过此帧")
|
Log.e(TAG, "❌ 队列清理后仍无法添加新帧,跳过此帧")
|
||||||
droppedFrameCount++
|
droppedFrameCount++
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1751,7 +1693,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
checkAndCleanMemory()
|
checkAndCleanMemory()
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 发送帧数据失败", e)
|
Log.e(TAG, "❌ 发送帧数据失败", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1762,7 +1704,6 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
if (queueProcessingStarted.compareAndSet(false, true)) {
|
if (queueProcessingStarted.compareAndSet(false, true)) {
|
||||||
Log.d(TAG, "🚀 启动屏幕数据队列处理协程")
|
Log.d(TAG, "🚀 启动屏幕数据队列处理协程")
|
||||||
queueProcessorJob = serviceScope.launch {
|
queueProcessorJob = serviceScope.launch {
|
||||||
try {
|
|
||||||
while (isCapturing && isActive) {
|
while (isCapturing && isActive) {
|
||||||
try {
|
try {
|
||||||
val frameData = screenDataQueue.poll()
|
val frameData = screenDataQueue.poll()
|
||||||
@@ -1772,23 +1713,16 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
// 队列为空,短暂休眠避免CPU占用过高
|
// 队列为空,短暂休眠避免CPU占用过高
|
||||||
delay(10)
|
delay(10)
|
||||||
}
|
}
|
||||||
} catch (e: CancellationException) {
|
|
||||||
// 协程正常取消,直接退出循环
|
|
||||||
throw e
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 队列处理帧异常: ${e.message}")
|
Log.e(TAG, "❌ 队列处理协程异常", e)
|
||||||
delay(100)
|
delay(100) // 出错时延长休眠时间
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: CancellationException) {
|
|
||||||
Log.d(TAG, "🛑 队列处理协程被取消(正常停止)")
|
|
||||||
} finally {
|
|
||||||
Log.d(TAG, "🛑 屏幕数据队列处理协程结束")
|
Log.d(TAG, "🛑 屏幕数据队列处理协程结束")
|
||||||
queueProcessingStarted.set(false)
|
queueProcessingStarted.set(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔧 停止队列处理协程
|
* 🔧 停止队列处理协程
|
||||||
@@ -1807,7 +1741,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
Log.d(TAG, "🗑️ 清空队列中剩余的${remainingFrames}帧数据")
|
Log.d(TAG, "🗑️ 清空队列中剩余的${remainingFrames}帧数据")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 停止队列处理协程失败", e)
|
Log.e(TAG, "❌ 停止队列处理协程失败", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1837,7 +1771,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
frameData.fill(0) // 清空数组内容
|
frameData.fill(0) // 清空数组内容
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 处理帧数据失败", e)
|
Log.e(TAG, "❌ 处理帧数据失败", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1881,7 +1815,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 内存检查失败", e)
|
Log.e(TAG, "❌ 内存检查失败", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1913,7 +1847,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
Log.w(TAG, "✅ 紧急内存清理完成")
|
Log.w(TAG, "✅ 紧急内存清理完成")
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 紧急内存清理失败", e)
|
Log.e(TAG, "❌ 紧急内存清理失败", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1961,7 +1895,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "⚠️ 清理WeakReference失败", e)
|
Log.e(TAG, "❌ 清理WeakReference失败", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2261,18 +2195,14 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
*/
|
*/
|
||||||
private fun cleanupVirtualDisplayOnly() {
|
private fun cleanupVirtualDisplayOnly() {
|
||||||
try {
|
try {
|
||||||
// 释放VirtualDisplay(停止生产者向Surface写帧)
|
// 清理VirtualDisplay
|
||||||
// 注意:不使用setSurface(null),某些设备上会导致MediaProjection session失效
|
|
||||||
virtualDisplay?.release()
|
virtualDisplay?.release()
|
||||||
virtualDisplay = null
|
virtualDisplay = null
|
||||||
|
|
||||||
// 紧接着关闭ImageReader(销毁Surface,生产者自然停止dequeueBuffer)
|
// 清理ImageReader
|
||||||
imageReader?.close()
|
imageReader?.close()
|
||||||
imageReader = null
|
imageReader = null
|
||||||
|
|
||||||
// 标记abandoned状态
|
|
||||||
bufferQueueAbandoned = true
|
|
||||||
|
|
||||||
// ✅ 安全清理缓存的Bitmap,防止多线程并发导致native crash (SIGSEGV)
|
// ✅ 安全清理缓存的Bitmap,防止多线程并发导致native crash (SIGSEGV)
|
||||||
safeRecycleLastValidBitmap()
|
safeRecycleLastValidBitmap()
|
||||||
lastCaptureTime = 0L
|
lastCaptureTime = 0L
|
||||||
@@ -2474,21 +2404,14 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
|
|||||||
|
|
||||||
Log.i(TAG, "🔧 Android 15:ImageReader重新创建完成")
|
Log.i(TAG, "🔧 Android 15:ImageReader重新创建完成")
|
||||||
|
|
||||||
// 重新创建VirtualDisplay(带Callback监听停止事件)
|
// 重新创建VirtualDisplay
|
||||||
val retryDisplayCallback = object : android.hardware.display.VirtualDisplay.Callback() {
|
|
||||||
override fun onStopped() {
|
|
||||||
Log.w(TAG, "🚨 Android 15重试VirtualDisplay.Callback.onStopped")
|
|
||||||
bufferQueueAbandoned = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtualDisplay = mediaProjection?.createVirtualDisplay(
|
virtualDisplay = mediaProjection?.createVirtualDisplay(
|
||||||
"RemoteControlCaptureRetry_${System.currentTimeMillis()}",
|
"RemoteControlCaptureRetry_${System.currentTimeMillis()}",
|
||||||
screenWidth, screenHeight,
|
screenWidth, screenHeight,
|
||||||
service.resources.displayMetrics.densityDpi,
|
service.resources.displayMetrics.densityDpi,
|
||||||
android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
|
android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
|
||||||
imageReader?.surface, retryDisplayCallback, backgroundHandler
|
imageReader?.surface, null, null
|
||||||
)
|
)
|
||||||
bufferQueueAbandoned = false
|
|
||||||
|
|
||||||
if (virtualDisplay != null) {
|
if (virtualDisplay != null) {
|
||||||
Log.i(TAG, "✅ Android 15 VirtualDisplay完全重新创建成功")
|
Log.i(TAG, "✅ Android 15 VirtualDisplay完全重新创建成功")
|
||||||
|
|||||||
Reference in New Issue
Block a user