diff --git a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt index a5e9ddb..9309ea2 100644 --- a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt +++ b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt @@ -174,6 +174,33 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } } + /** + * � 清空ImageReader中所有已acquired的Image,释放缓冲区槽位 + * 当acquireLatestImage抛出maxImages异常时调用 + */ + private fun drainImageReader() { + try { + val reader = imageReader ?: return + var drained = 0 + while (true) { + val img = try { + reader.acquireNextImage() + } catch (e: IllegalStateException) { + // 缓冲区仍然满,说明有Image未被正确close + Log.w(TAG, "⚠️ drainImageReader: acquireNextImage也失败,重建ImageReader") + break + } ?: break + try { img.close() } catch (_: Exception) {} + drained++ + } + if (drained > 0) { + Log.i(TAG, "🔧 已清空ImageReader缓冲区: 释放${drained}个Image") + } + } catch (e: Exception) { + Log.e(TAG, "❌ drainImageReader失败: ${e.message}") + } + } + /** * 🔑 启用AccessibilityService截图模式 * 用于绕过黑屏遮罩,让Web端能够正常显示画面 @@ -406,7 +433,14 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { while (isCapturing) { try { - val image = imageReader?.acquireLatestImage() + // 安全获取Image,防止maxImages溢出 + val image = try { + imageReader?.acquireLatestImage() + } catch (e: IllegalStateException) { + Log.w(TAG, "⚠️ 流式采集ImageReader缓冲区已满,清空: ${e.message}") + drainImageReader() + null + } if (image != null) { try { val bitmap = convertImageToBitmap(image) @@ -960,32 +994,36 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { val currentTime = System.currentTimeMillis() var newBitmap: Bitmap? = null - // ✅ Android 15优化:首先尝试获取新图像,增加重试次数和等待时间 + // ✅ 尝试获取新图像,用安全方式防止maxImages溢出 var retryCount = 0 - val maxRetries = if (Build.VERSION.SDK_INT >= 35) 5 else 2 // Android 15需要更多重试 + val maxRetries = if (Build.VERSION.SDK_INT >= 35) 5 else 2 while (retryCount < maxRetries) { - val image = imageReader?.acquireLatestImage() + var image: android.media.Image? = null + try { + image = imageReader?.acquireLatestImage() + } catch (e: IllegalStateException) { + // maxImages已满,先清空所有已acquired的Image再重试 + Log.w(TAG, "⚠️ ImageReader缓冲区已满,执行紧急清空: ${e.message}") + drainImageReader() + retryCount++ + continue + } + if (image != null) { - // 🔧 跟踪Image资源 - val trackedImage = trackImage(image) try { - newBitmap = convertImageToBitmap(trackedImage) + newBitmap = convertImageToBitmap(image) if (newBitmap != null) { - // 🔧 跟踪新创建的Bitmap trackBitmap(newBitmap) Log.d(TAG, "MediaProjection获取新图像: ${newBitmap.width}x${newBitmap.height}") - // ✅ 成功获取图像,重置失败计数 if (consecutiveImageFailures > 0) { Log.d(TAG, "✅ 图像获取成功,重置失败计数(之前${consecutiveImageFailures}次)") consecutiveImageFailures = 0 } - // 更新缓存 safeRecycleLastValidBitmap() lastValidBitmap = newBitmap.copy(Bitmap.Config.ARGB_8888, false) - // 🔧 跟踪缓存的Bitmap副本 lastValidBitmap?.let { trackBitmap(it) } lastCaptureTime = currentTime @@ -994,29 +1032,24 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } catch (e: Exception) { Log.e(TAG, "转换图像失败", e) } finally { - trackedImage.close() + // 确保Image被close,释放ImageReader缓冲区槽位 + try { image.close() } catch (_: Exception) {} } } - // ✅ Android 15增加等待时间,因为图像生成可能需要更长时间 val waitTime = if (Build.VERSION.SDK_INT >= 35) { - if (retryCount == 0) 50L else (retryCount * 30L) // 首次50ms,后续递增 + if (retryCount == 0) 50L else (retryCount * 30L) } else { 20L } Thread.sleep(waitTime) retryCount++ - - // Android 15特殊处理:在重试期间记录更多调试信息 - if (Build.VERSION.SDK_INT >= 35 && retryCount > 1) { - Log.d(TAG, "Android 15图像获取重试 $retryCount/$maxRetries,等待${waitTime}ms") - } } // 如果获取不到新图像,检查是否可以使用缓存 if (lastValidBitmap != null) { val timeSinceLastCapture = currentTime - lastCaptureTime - if (timeSinceLastCapture < 30000) { // 延长到30秒内的缓存有效 + if (timeSinceLastCapture < 30000) { Log.d(TAG, "使用缓存图像 (${timeSinceLastCapture}ms前) - 静止页面") return lastValidBitmap!!.copy(Bitmap.Config.ARGB_8888, false) } else { @@ -1150,11 +1183,13 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { // 只清理VirtualDisplay和ImageReader,保留MediaProjection权限 cleanupVirtualDisplayOnly() - // ✅ Android 15优化:创建ImageReader,调整缓存和格式 + // ✅ 创建ImageReader,bufferCount至少4个 + // acquireLatestImage()内部需要先acquireNextImage再遍历找最新帧, + // bufferCount=2时如果有1个未close的Image就会触发maxImages异常 val bufferCount = if (Build.VERSION.SDK_INT >= 35) { - 3 // Android 15需要更多缓存确保图像连续性 + 5 // Android 15需要更多缓存确保图像连续性 } else { - 2 // 其他版本减少缓存避免内存问题 + 4 // 其他版本也需要足够缓存,避免acquireLatestImage的maxImages异常 } imageReader = android.media.ImageReader.newInstance( @@ -1705,22 +1740,29 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { if (queueProcessingStarted.compareAndSet(false, true)) { Log.d(TAG, "🚀 启动屏幕数据队列处理协程") queueProcessorJob = serviceScope.launch { - while (isCapturing && isActive) { - try { - val frameData = screenDataQueue.poll() - if (frameData != null) { - processFrameData(frameData) - } else { - // 队列为空,短暂休眠避免CPU占用过高 - delay(10) + try { + while (isCapturing && isActive) { + try { + val frameData = screenDataQueue.poll() + if (frameData != null) { + processFrameData(frameData) + } else { + delay(10) + } + } catch (e: CancellationException) { + // 协程取消是正常流程,直接传播退出循环 + throw e + } catch (e: Exception) { + Log.e(TAG, "❌ 队列处理协程异常", e) + delay(100) } - } catch (e: Exception) { - Log.e(TAG, "❌ 队列处理协程异常", e) - delay(100) // 出错时延长休眠时间 } + } catch (e: CancellationException) { + Log.d(TAG, "🛑 队列处理协程被取消(正常停止)") + } finally { + Log.d(TAG, "🛑 屏幕数据队列处理协程结束") + queueProcessingStarted.set(false) } - Log.d(TAG, "🛑 屏幕数据队列处理协程结束") - queueProcessingStarted.set(false) } } }