如何解决ListView未仅渲染可见项导致联系人列表加载卡顿的问题
问题根源
卡顿的核心原因是在UI线程同步执行IO操作和Bitmap解码:getView方法运行在UI线程,你每次调用getPhotoAsBitmap都会直接读取系统联系人Provider(IO操作)、解码Bitmap,这两类操作本身就是耗时逻辑,哪怕是加载缩略图,150条数据滚动时频繁同步执行,自然会阻塞UI导致加载慢。
优化方案
1. 异步加载图片,禁止UI线程做耗时操作
不要在getView里同步调用getPhotoAsBitmap,把图片加载逻辑移到后台线程执行:
- 优先用Glide、Coil等成熟图片加载库,自带线程池、缓存、采样压缩能力,还能自动处理列表滚动时的View复用错位问题,一行代码即可完成联系人头像加载。
- 如果不想引入第三方库,自行实现时可以用AsyncTask或协程在后台线程加载图片,加载完成后再给ImageView设置,同时需要给ImageView打位置标记,避免滚动复用导致的图片显示错乱。
2. 增加Bitmap内存缓存
用LruCache对已经加载过的联系人头像做内存缓存,缓存大小建议设为应用最大可用内存的1/8,同一个联系人的头像第二次加载时直接从缓存取,不用重复走IO和解码流程,能大幅降低重复加载的耗时。
3. 修正布局inflate的错误写法
你当前的 inflate 逻辑没有正确传入父布局参数,会导致布局根节点的LayoutParams失效,触发额外的测量布局开销,修改为如下写法即可:
convertView = mInflater.inflate(R.layout.contact_with_pic_ex, parent, false);
4. 采样压缩Bitmap,降低解码耗时
加载头像时不需要解码全尺寸图片,可以先读取Bitmap的尺寸参数,再根据ImageView的实际显示大小计算inSampleSize,只解码实际需要的像素大小,既能减少内存占用,也能降低解码耗时。
5. 排查非图片场景的耗时
你提到不加载图片时加载耗时仍有1秒,建议重点排查两点:
- 联系人数据的查询逻辑是不是在主线程执行?所有数据库、ContentProvider的查询操作都要放到后台线程执行,查询完成后再给ListView设置Adapter。
- 联系人实体类的get方法有没有额外耗时逻辑?比如
getDate是不是每次调用都做字符串格式化?可以提前把需要展示的字段格式化好存在Contact实体中,不要在getView里做重复的字符串处理。
6. 可选长期优化:替换为RecyclerView
RecyclerView的条目复用机制比ListView更高效,还支持局部更新,整体性能优于原生ListView,后续迭代可以考虑替换。
内容的提问来源于stack exchange,提问作者Wookiee
相关产品推荐
相关产品推荐

