如何提升RecyclerView中使用fromHTML实现的ImageGetter运行效率
优化RecyclerView中Html.fromHtml加载带图HTML的性能方案
核心问题原因
原生Html.fromHtml默认在调用线程执行,若直接在主线程/RecyclerView的onBindViewHolder中调用,HTML解析+ImageGetter同步拉取图片的逻辑会全部阻塞主线程,再加上RecyclerView绑定过程的频繁调用,必然会出现主线程过载、卡顿甚至崩溃的问题。另外ImageGetter要求同步返回Drawable的设计,本身和主流异步图片加载库的逻辑冲突,也是你无法直接拿到Drawable的核心原因。
具体优化方案
1. 把HTML解析逻辑完全迁移到子线程
- 不要在主线程或绑定逻辑中调用
Html.fromHtml,拿到列表数据源后,直接通过线程池/协程批量完成所有条目的HTML解析,把解析后的Spanned对象缓存到你的条目数据类中,绑定ViewHolder时直接给TextView设置预解析好的Spanned即可。 - Android 7.0及以上版本
Html.fromHtml本身是线程安全的,可放心在子线程调用。
2. 实现异步ImageGetter,适配主流图片加载库
不需要等待图片加载完成再返回Drawable,先同步返回占位Drawable,异步加载完成后再局部刷新TextView即可,以下是Glide的实现示例:
class GlideImageGetter( private val textView: TextView, private val glide: RequestManager ) : Html.ImageGetter { override fun getDrawable(source: String?): Drawable { // 先返回占位Drawable,提前设置好默认尺寸 val placeholder = ContextCompat.getDrawable(textView.context, R.drawable.default_placeholder)!! placeholder.setBounds(0, 0, placeholder.intrinsicWidth, placeholder.intrinsicHeight) // 异步加载目标图片 glide.asDrawable() .load(source) .into(object : CustomTarget<Drawable>() { override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) { resource.setBounds(0, 0, resource.intrinsicWidth, resource.intrinsicHeight) // 替换Spannable中的占位ImageSpan val spannable = textView.text as Spannable val targetSpan = spannable.getSpans(0, spannable.length, ImageSpan::class.java) .find { it.drawable == placeholder } targetSpan?.let { spannable.removeSpan(it) spannable.setSpan( ImageSpan(resource), spannable.getSpanStart(it), spannable.getSpanEnd(it), spannable.getSpanFlags(it) ) } // 仅触发当前TextView重绘,不需要刷新整个RecyclerView条目 textView.text = spannable } override fun onLoadCleared(placeholder: Drawable?) = Unit }) return placeholder } }
Coil、Picasso都可以参考这个逻辑实现,不需要修改原有加载逻辑。
3. 优化RecyclerView更新逻辑
- 不要全量调用
notifyDataSetChanged(),使用DiffUtil计算新旧数据集差异,仅更新变化的条目,减少不必要的绑定操作。 - 全量更新数据集时,把
DiffUtil.calculateDiff逻辑放到子线程执行,计算完差异后再切回主线程调用dispatchUpdatesTo(adapter)更新,避免大数据量下Diff计算阻塞主线程。 - 分页加载数据时调用
notifyItemRangeInserted()做局部刷新,不要触发全量更新。 - 可监听RecyclerView滑动状态,滑动过程中暂停图片加载,滑动停止后再恢复,减少主线程压力。
4. 额外优化点
- 可预先解析HTML中的所有图片URL,提前预加载到图片库的内存缓存中,ImageGetter调用时可直接拿到缓存的Drawable,不需要等待网络请求。
- 加载图片时通过图片库的
override()方法限制最大尺寸,避免加载过大图片占用过多内存、增加绘制耗时。
内容的提问来源于stack exchange,提问作者Tamzid
相关产品推荐
相关产品推荐

