Pixel Copy崩溃排查:窗口无后备表面问题求助
问题原因与修复方案
崩溃核心原因
Window doesn't have a backing surface! 异常本质是:调用PixelCopy.request时,目标窗口的底层Surface已被释放、未完成初始化,或窗口处于不可见/非活跃状态。
你用decorView.rootView.post仅能保证View树绘制完成,但无法确保窗口Surface状态稳定——比如:
- Activity进入
onDestroy流程,Surface已被销毁 - 窗口被切换到后台(如按Home键),Surface临时释放
- 屏幕旋转等配置变化过程中,旧窗口Surface失效、新窗口Surface未创建
具体修复方案
1. 增加窗口有效性检查+异常捕获
在调用PixelCopy前先校验窗口状态,并捕获Surface异常避免崩溃:
@RequiresApi(Build.VERSION_CODES.O) object PixelCopyUtils { fun getViewBitmap(window: Window, event: (PixelCopyEvents) -> Unit) { val decorView = window.decorView // 先判断窗口是否处于活跃可见状态 if (decorView.visibility != View.VISIBLE || !window.isActive) { event(PixelCopyEvents.OnCopyError) return } val rootView = decorView.rootView val bitmap = Bitmap.createBitmap(rootView.width, rootView.height, Bitmap.Config.ARGB_8888) val handlerThread = HandlerThread(PixelCopyUtils::class.java.simpleName) handlerThread.start() try { PixelCopy.request(window, bitmap, { copyResult -> if (copyResult == PixelCopy.SUCCESS) { event(PixelCopyEvents.OnCopySuccess(bitmap)) } else { event(PixelCopyEvents.OnCopyError) } handlerThread.quitSafely() }, Handler(handlerThread.looper)) } catch (e: IllegalArgumentException) { // 捕获Surface无效的异常,避免崩溃 event(PixelCopyEvents.OnCopyError) handlerThread.quitSafely() } catch (e: IllegalStateException) { // 处理其他窗口状态异常 event(PixelCopyEvents.OnCopyError) handlerThread.quitSafely() } } sealed interface PixelCopyEvents { object OnCopyError : PixelCopyEvents data class OnCopySuccess(val bitmap: Bitmap) : PixelCopyEvents } }
2. 优化调用时机的前置校验
修改扩展函数,提前过滤无效窗口状态:
@RequiresApi(Build.VERSION_CODES.O) fun Window.onViewDraw(isReady: (Bitmap?) -> Unit) { val decorView = this.decorView if (decorView.visibility != View.VISIBLE || !this.isActive) { logging(TAG, "Window is not active or visible, can't take screenshot.") isReady(null) return } decorView.rootView.post { PixelCopyUtils.getViewBitmap(this) { event -> when (event) { PixelCopyUtils.PixelCopyEvents.OnCopyError -> { logging(TAG, "Error while getting the screenshot through PixelCopy API.") isReady(null) } is PixelCopyUtils.PixelCopyEvents.OnCopySuccess -> { isReady(event.bitmap) } } } } }
3. 可选:增加偶发异常的重试机制
如果是Surface状态波动导致的偶发失败,可增加一次短延迟重试:
@RequiresApi(Build.VERSION_CODES.O) object PixelCopyUtils { private const val MAX_RETRY = 1 private var retryCount = 0 fun getViewBitmap(window: Window, event: (PixelCopyEvents) -> Unit) { val decorView = window.decorView if (decorView.visibility != View.VISIBLE || !window.isActive) { resetRetryCount() event(PixelCopyEvents.OnCopyError) return } val rootView = decorView.rootView val bitmap = Bitmap.createBitmap(rootView.width, rootView.height, Bitmap.Config.ARGB_8888) val handlerThread = HandlerThread(PixelCopyUtils::class.java.simpleName) handlerThread.start() try { PixelCopy.request(window, bitmap, { copyResult -> if (copyResult == PixelCopy.SUCCESS) { resetRetryCount() event(PixelCopyEvents.OnCopySuccess(bitmap)) } else { handleRetry(window, event, handlerThread) } handlerThread.quitSafely() }, Handler(handlerThread.looper)) } catch (e: IllegalArgumentException) { handleRetry(window, event, handlerThread) } } private fun handleRetry(window: Window, event: (PixelCopyEvents) -> Unit, handlerThread: HandlerThread) { if (retryCount < MAX_RETRY) { retryCount++ Handler(handlerThread.looper).postDelayed({ getViewBitmap(window, event) }, 100) } else { resetRetryCount() event(PixelCopyEvents.OnCopyError) handlerThread.quitSafely() } } private fun resetRetryCount() { retryCount = 0 } sealed interface PixelCopyEvents { object OnCopyError : PixelCopyEvents data class OnCopySuccess(val bitmap: Bitmap) : PixelCopyEvents } }
额外注意事项
- 避免在
onPause、onDestroy或配置变化回调中触发截图 - 使用完截图Bitmap后,记得调用
bitmap.recycle()回收内存 - 多窗口模式下需额外校验窗口的显示层级
内容的提问来源于stack exchange,提问作者MACROSystems
相关产品推荐
相关产品推荐

