You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 8设备Jetpack Compose捕获Composable时硬件位图异常修复求助

修复Android 8上Composable捕获Picture时的硬件位图异常

问题描述

我实现了一个Modifier扩展方法,用于将Composable内容捕获到Picture实例中,代码如下:

public fun Modifier.captureDrawableContent(picture: Picture, vararg keys: Any?): Modifier = composed {
    val keyHash = keys.contentHashCode()

    remember(keyHash) {
        Modifier.drawWithCache {
            val width = size.width.toInt()
            val height = size.height.toInt()

            onDrawWithContent {
                val pictureCanvas = Canvas(picture.beginRecording(width, height))
                draw(this, layoutDirection, pictureCanvas, size) {
                    this@onDrawWithContent.drawContent()
                }
                picture.endRecording()

                drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) }
            }
        }
    }
}

该方法在多数设备上运行正常,但在Android 8(API 26)设备上抛出以下异常:

Fatal Exception: java.lang.IllegalStateException: Software rendering doesn't support hardware bitmaps

异常触发在draw(this, layoutDirection, pictureCanvas, size)代码块内,Android 7.1.1设备无此问题。

问题原因

Android 8的软件渲染器存在限制:无法处理硬件位图。而Compose默认会使用硬件位图提升绘制性能,当我们将绘制命令重定向到基于Picture的软件Canvas时,硬件位图与软件渲染器的冲突就会触发该异常。Android 7.1.1及以下版本的软件渲染器没有这个限制,因此可以正常运行。

修复方案

方案1:强制关闭目标Composable的硬件加速

通过graphicsLayer修饰符强制关闭硬件加速,让Composable使用软件渲染生成兼容的位图,这样录制到Picture时就不会触发异常:

public fun Modifier.captureDrawableContent(picture: Picture, vararg keys: Any?): Modifier = composed {
    val keyHash = keys.contentHashCode()

    remember(keyHash) {
        Modifier
            // 禁用硬件加速,强制使用软件渲染生成兼容位图
            .graphicsLayer(hardwareAccelerationEnabled = false)
            .drawWithCache {
                val width = size.width.toInt()
                val height = size.height.toInt()

                onDrawWithContent {
                    val pictureCanvas = Canvas(picture.beginRecording(width, height))
                    draw(this, layoutDirection, pictureCanvas, size) {
                        this@onDrawWithContent.drawContent()
                    }
                    picture.endRecording()

                    drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) }
                }
            }
    }
}

方案2:通过中间软件Bitmap中转绘制

如果不想关闭硬件加速,可以先将Composable内容绘制到软件配置的Bitmap中,再将Bitmap内容录制到Picture:

public fun Modifier.captureDrawableContent(picture: Picture, vararg keys: Any?): Modifier = composed {
    val keyHash = keys.contentHashCode()

    remember(keyHash) {
        Modifier.drawWithCache {
            val width = size.width.toInt()
            val height = size.height.toInt()

            onDrawWithContent {
                // 创建软件配置的Bitmap,确保兼容性
                val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
                val bitmapCanvas = Canvas(bitmap)
                
                // 将Composable内容绘制到软件Bitmap
                draw(this, layoutDirection, bitmapCanvas, size) {
                    this@onDrawWithContent.drawContent()
                }
                
                // 将Bitmap内容录制到Picture
                val recorder = picture.beginRecording(width, height)
                recorder.drawBitmap(bitmap, 0f, 0f, null)
                picture.endRecording()
                
                // 绘制到原Canvas(如需显示)
                drawIntoCanvas { canvas ->
                    canvas.nativeCanvas.drawPicture(picture)
                }
                
                // 回收Bitmap避免内存泄漏
                bitmap.recycle()
            }
        }
    }
}

注意事项

  • 方案1的硬件加速关闭仅影响当前Composable,不会全局生效,适合快照捕获场景,性能影响可控。
  • 方案2需要注意Bitmap的内存回收,避免内存泄漏,尤其是在频繁捕获的场景下。

内容的提问来源于stack exchange,提问作者Serhii Buvaka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 15:45:22