• Lv0
    粉丝20

积分1156 / 贡献0

提问0答案被采纳63文章8

[经验分享] 使用三方库Imageknife实现列表动态预加载 原创 精华

马迪 显示全部楼层 发表于 2024-9-17 15:47:41

背景

列表是应用开发中最常见的一类开发场景,它可以将杂乱的信息整理成有规律、易于理解和操作的形式,便于用户查找和获取所需要的信息。应用程序中常见的列表场景有新闻列表、购物车列表、各类排行榜等。随着信息数据的累积,特别是一些新闻应用、购物应用、聊天应用,列表数据往往会达到上万条,针对这类大量数据加载的长列表应用,如何对长列表的性能进行优化是非常重要的。一个正确、高性能的长列表应用能明显降低列表渲染时间、提升页面的滑动帧率、降低应用内存占用,大幅提升用户体验。 针对长列表加载这一场景,通常会使用5种优化手段,通过这些优化手段的单个使用或组合使用,可以对列表渲染时间、页面滑动帧率、应用内存占用等方面带来优化,提升性能和用户体验:

  • 懒加载:提供列表数据按需加载能力,解决一次性加载长列表数据耗时长、占用过多资源的问题,可以提升页面响应速度。
  • 缓存列表项:提供屏幕可视区域外列表项长度的自定义调节能力,配合懒加载设置可缓存列表项参数,通过预加载数据提升列表滑动体验。
  • 动态预加载:根据历史任务加载耗时情况,动态调整屏幕可视区域外数据预取数量,配合懒加载设置,可在列表不断滑动时,屏幕可视区外实时更新列表数据,通过预取和预渲染数据提升列表滑动体验。
  • 组件复用:提供可复用组件对象的缓存资源池,通过重复使用已经创建过并缓存的组件对象,降低相同组件短时间内频繁创建和销毁的开销,提升组件渲染效率。
  • 布局优化:使用扁平化布局方案,减少视图嵌套层级和组件数,避免过度绘制,提升页面渲染效率。

预加载方案

而三方库ImageKnife自带图片缓存特性,结合懒加载的使用,可以有效提升图片加载的效率,解决白块问题。此外ImageKnife还提供了preLoadCache方法可以预加载图片。本文将介绍ImageKnife的preLoadCache预加载方法如何结合列表LazyForEach懒加载使用:

  • 实现DataSourcePrefetchingImageKnife类,继承IDataSourcePrefetching接口,并实现prefetch和cancel方法 其中 prefetch做图片的请求和缓存处理。cancel在组件不可视区域取消请求。
export default class DataSourcePrefetchingImageKnife implements IDataSourcePrefetching {
  private dataArray: Array<InfoItem>
  private listeners: DataChangeListener[] = [];

  constructor(dataArray: Array<InfoItem>) {
    this.dataArray = dataArray;
  }
  public getData(index: number) {
    return this.dataArray[index]
  }

  public totalCount(): number {
    return this.dataArray.length
  }

  public addData(index: number, data: InfoItem[]): void {
    this.dataArray = this.dataArray.concat(data)
    this.notifyDataAdd(index)
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      this.listeners.splice(pos, 1);
    }
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      this.listeners.push(listener)
    }
  }

  notifyDataAdd(index: number): void {
    this.listeners.forEach((listener: DataChangeListener) => {
      listener.onDataAdd(index)
    })
  }
  async prefetch(index: number): Promise<void> {
    let item = this.dataArray[index]
    try {
      if (typeof item.albumUrl == "string") {
        // 图片预加载
        await ImageKnife.getInstance().preLoadCache(item.albumUrl)
      }
    } catch (err) {
      // 预加载失败,展示错误图
      item.albumUrl = IMADE_UNAVAILABLE
    }
  }
  // 取消请求处理
  cancel(index: number) {
  //   ImageKnifeComponent 内部触发aboutToDisAppear生命周期会将请求取消
  }
}
  • 在应用列表界面,首先创建DataSourcePrefetchingImageKnife、BasicPrefetcher对象,然后在List的onScrollIndex回调中调用BasicPrefetcher的visibleAreaChanged方法,传入List的可见区域起始坐标。至此完成代码的优化。
@ComponentV2
export struct PrefetchAndPreload {
  // 创建DataSourcePrefetchingImageKnife对象,具备任务预取、取消能力的数据源
  private readonly dataSource = new DataSourcePrefetchingImageKnife(PageViewModel.getItems());
  // 创建BasicPrefetcher对象,默认的动态预取算法实现
  private readonly prefetcher = new BasicPrefetcher(this.dataSource);

  build() {
    Column() {
      List({ space: 16 }) {
        LazyForEach(this.dataSource, (item: InfoItem,index:number) => {
          ListItem() {
            Column({ space: 12 }) {
              ImageKnifeComponent({
                imageKnifeOption:new ImageKnifeOption({
                  loadSrc: item.albumUrl,
                  placeholderSrc:$r('app.media.loading')
                })
              }).width(100).height(100)
              Text(`${index}`)
            }.border({ width: 5 , color: "#000000"})
          }
          .reuseId('imageKnife')
        })
      }
      .cachedCount(5)
      .onScrollIndex((start: number, end: number) => {
        // 列表滚动触发visibleAreaChanged,实时更新预取范围,触发调用prefetch、cancel接口
        this.prefetcher.visibleAreaChanged(start, end)
      })
      .width("100%")
      .height("100%")
      .margin({ left: 10, right: 10 })
      .layoutWeight(1)
    }
  }
}

实现原理

参考长列表优化概述

预加载效果

如下对比场景设置数据cachedCount=5、cachedCount=30和动态预加载:

cachedCount=5 cachedCount=30 动态预加载
1.gif 2.gif 3.gif
数据设置 首屏加载 滑动过程滑块数量
cachedCount=5 首屏加载快(首屏加载可视区+5张) 滑动过程中白块很多
cachedCount=30 首屏概率加载慢(首屏加载可视区+30张) 滑动过程中没有白块或很少
动态预加载 首屏加载快(首屏加载可视区+5张;再加载不可视区域图片) 滑动过程中没有白块或很少

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部