Flutter加载超大Dart字体数据文件阻塞应用的优化方案咨询
可行优化方案(按落地收益从高到低排序)
优先替换硬编码常量列表的存储形式
你现在遇到的30秒阻塞,核心原因不是15M的文件体积,是Dart解析代码中超长const数组字面量的开销:Dart需要在Isolate启动阶段逐元素解析、构建常量数组对象,这个过程本身就会占用大量CPU时间,和你解析FNT文件展开数据的开销本质一致——都是一次性把全量数据转换为Dart堆内的结构化对象。
直接把预生成的各字号字体数据存为平铺的原始二进制文件打包为资源,不要写在Dart代码里:用到哪个字号就通过rootBundle.load()读取对应资源,直接转为Uint8List使用。Uint8List是Dart底层实现的定长字节数组,解析开销比普通List<int>低一个数量级,单份1M左右的字号数据读取耗时在毫秒级。搭配简单的LRU缓存策略,仅保留最近使用的字号数据在内存,还能降低常驻内存占用。取消全字号预栅格化逻辑,改为按需生成+缓存
预存15种字号的全量字符位图本身性价比极低:同一款字体的TTF/OTF原始文件通常仅几MB,远小于你预生成的15M位图数据。你完全可以只打包1份原始字体文件,用到对应字号、对应字符时,再通过image库的文本绘制能力实时栅格化生成字符位图,同时用字典缓存已经栅格化过的字符结果,重复使用时直接读取即可。
单字符栅格化耗时在微秒级,就算单帧渲染上百个不同字符,总耗时也不会超过10ms,完全无感知,还能顺带缩减安装包体积。必须全量加载时把解析逻辑移到后台Isolate
如果业务要求必须一次性加载全量字体数据,不要在主UI Isolate执行读取、解析操作:用compute或者独立后台Isolate承担全量解析工作,解析完成后通过TransferableTypedData把字节数据零拷贝传回主Isolate,全程不会阻塞UI渲染,用户感知不到卡顿。非必要不接入原生实现
以上三个Dart层方案落地后,加载耗时基本可以压到1秒内,完全不需要接入Android/iOS原生渲染库。原生方案需要维护双端代码,跨端通信传位图也有额外开销,收益极低,仅在极端性能要求场景下考虑。
内容的提问来源于stack exchange,提问作者Dabbel

