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
相关产品推荐
相关产品推荐

