列表滑动性能优化之预加载图片空闲延迟解码

34 阅读1分钟

一、流程图

mermaid-1782371530607.png

二、优化核心思想

Runloop空闲调度+后台解码+主线程回调

三、优化步骤

一、TableView 预先加载图片

`

    imageLoader.prefetch(urls: urls, targetPixelSize: CGSize(width: 240, height: 240))

`

FeedCell绑定数据的时候开始图片加载使用组件ImageLoader ImageLoader 在网络下载到 data 后,不直接立刻在后台解码,而是先 enqueue 到 RunLoopIdleWorkScheduler ,等主线程 RunLoop 即将进入 idle( beforeWaiting )时才开始把解码任务派发到 decodeQueue 。 这会导致:滚动过程中主线程 RunLoop 很忙(持续处理触摸/滚动/布局/绘制),不容易进入 beforeWaiting ,因此解码任务堆积;当你停下来,RunLoop 进入空闲点,积压的解码开始执行,于是“停下来才出图”。

二、RunLoop监听空闲beforeWaiting

只有滑动停止的时候才开始批量解码回调到主线程展示图片。Scheduler 监听 CFRunLoopActivity.beforeWaiting ,每次主线程准备休眠前执行少量任务( maxCount: 2 ),避免滚动期间抢主线程时机。

let obs = CFRunLoopObserverCreate( kCFAllocatorDefault, CFRunLoopActivity.beforeWaiting.rawValue, true, 0, { _, _, info in let scheduler = ... scheduler.drain(maxCount: 2) }, &context )

三、mageLoader:下载后先 enqueue,再后台 downsample 解码

  • URLSession 下载完成后把“解码任务”塞到 RunLoopIdleWorkScheduler.shared.enqueue { ... }
  • 真正的降采样解码发生在 decodeQueue.async { ... }
  • 最后 finish(...) 再 DispatchQueue.main.async 回调给 cell 去设置 iv.image

为什么滚动时 cell 还是能“加载”,但看起来不显示图?

这里“加载”分两段:

  • 网络请求可能已经开始甚至完成 (prefetch 甚至会提前把请求打出去)
  • 但解码与 UI 设置被延迟
    • 解码要等主线程空闲点才会被安排(enqueue → drain)
    • UI 设置必须回到主线程( DispatchQueue.main.async ) 所以用户观感通常就是:滚动时 cell 文本先出来;停下来后图片开始逐个补齐

项目链接