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

Android LazyColumn加载大量字体引发OutOfMemoryError的解决方法

解决Jetpack Compose LazyColumn展示大量TTF字体导致的OutOfMemoryError问题

问题根源

直接为每个列表项加载完整的TTF字体资源,即使LazyColumn是懒加载模式,快速滚动时大量字体实例会驻留内存,加上TTF文件本身占用的内存较高,最终触发内存溢出。


具体解决方案

1. 实现字体实例缓存复用

创建LRU缓存池,避免重复加载同一字体文件,自动回收最少使用的实例,控制内存占用上限。

// 全局或可复用的字体缓存,设置最大缓存数量(按需调整)
private val fontCache = object : LruCache<String, Font>(20) {
    override fun sizeOf(key: String, value: Font): Int {
        // 估算单字体占用内存,可根据实际文件大小调整
        return value.file.size().toInt()
    }
}

// 封装字体加载逻辑,优先从缓存获取
private fun loadFont(context: Context, fontResId: Int): Font {
    val key = fontResId.toString()
    fontCache.get(key)?.let { return it }
    val font = Font(context.resources, fontResId)
    fontCache.put(key, font)
    return font
}

在LazyColumn的列表项中调用loadFont,而非每次新建Font实例。

2. 按需释放离开可视区域的字体资源

利用Compose的DisposableEffect,在列表项被回收(离开屏幕)时,从缓存中移除对应字体实例,主动释放内存:

LazyColumn {
    items(fontResList) { resId ->
        val context = LocalContext.current
        var font by remember { mutableStateOf<Font?>(null) }

        // 加载字体
        LaunchedEffect(resId) {
            font = loadFont(context, resId)
        }

        // 列表项回收时释放资源
        DisposableEffect(resId) {
            onDispose {
                fontCache.remove(resId.toString())
                font = null
            }
        }

        font?.let { currentFont ->
            Text(
                text = "预览: ${currentFont.name}",
                fontFamily = FontFamily(currentFont),
                fontSize = 16.sp
            )
        } ?: Text(text = "加载中...")
    }
}

3. 用字体预览图替代完整字体加载

如果仅需展示字体效果,无需完整加载TTF文件,可生成文字预览Bitmap缓存,大幅降低内存占用:

// 生成字体预览Bitmap
private fun generateFontPreview(context: Context, fontResId: Int): Bitmap {
    val previewText = "AaBbCc"
    val paint = Paint().apply {
        textSize = 48f
        typeface = Typeface.createFromResource(context.resources, fontResId)
        isAntiAlias = true
    }
    val textBounds = Rect()
    paint.getTextBounds(previewText, 0, previewText.length, textBounds)
    
    return Bitmap.createBitmap(
        textBounds.width() + 20,
        textBounds.height() + 20,
        Bitmap.Config.ARGB_8888
    ).apply {
        val canvas = Canvas(this)
        canvas.drawText(previewText, 10f, textBounds.height() + 10f, paint)
    }
}

在列表项中缓存Bitmap而非Font实例,展示预览图即可。

4. 临时缓解:启用大内存堆(不推荐作为核心方案)

在AndroidManifest.xml的application标签中添加属性,临时扩大应用内存上限,但会占用更多系统资源:

<application
    ...
    android:largeHeap="true">

总结

优先采用缓存复用+按需释放的组合方案,结合预览图优化,从根源上减少内存占用,避免OutOfMemoryError。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 16:09:50