智能网络感知,打造极致流畅的鸿蒙原生版中国移动云盘图文体验

智能网络感知,打造极致流畅的鸿蒙原生版中国移动云盘图文体验

背景

中国移动云盘(原“和彩云网盘”)是中国移动重磅推出的安全、智能、不限速、移动用户免流的智能云盘,致力于成为5G时代用户个人与家庭的数字资产管理中心,是中国移动继语音、短信、流量后的“第四项基础服务”。

照片、音视频的备份和使用是市面上所有网盘用户的重点体验场景,用户将照片等资源备份到云盘,以节省本地资源存储。批量的图片、音视频缩略图加载,有时因网络原因出现加载缓慢等异常。中国移动云盘技术团队,致力于实现极致流畅的图文加载体验持续优化。

解决方案

以中国移动云盘鸿蒙原生版为例,借助华为Remote Communication Kit提供的智能网络感知服务,中国移动云盘通过三个步骤,提升了用户使用云盘时的加载体验,为用户提供更丝滑的云盘服务。

步骤一:动态预加载机制

以中国移动云盘的相册为例,当用户打开相册时,图文加载区域分为预加载区域、预渲染区域以及可见区域。

首先,智能网络感知服务帮助中国移动云盘优先保证用户可见区域的加载完成,并跟随屏幕滑动动态调整预加载区域,同时通过本地文件缓存替代实时加载的形式,在屏幕滑动时大大降低了图片白块概率;再通过减少预渲染区域预加载的图片数量,降低UI渲染带来的CPU负载开销。

步骤二:动态取消加载机制

当用户快速滑动屏幕时,有时候有的资源已经滑出了屏幕,但网络请求还未到达。中国移动云盘基于智能网络感知服务的网络请求可取消机制,在用户快速滑动时,将滑过屏幕的资源取消加载,降低网络负载。

步骤三:智能网络感知下动态调控预加载区域机制

基于RCP的网络耗时统计能力,可以计算出每个资源请求的平均耗时,感知网络质量并智能调整预加载区域。在网络质量好时,增大预加载区域,网络质量差时,就降低预加载区域。

成果展示

最终,通过中国移动云盘专家与华为网络通信专家联合构建图文资源动态加载,实现了鸿蒙原生版中国移动云盘网络资源如本地资源一样的流畅加载体验。

 优化前:有时因网络原因出现加载占位图和加载过程动画。

优化后:随意滑动,图文内容加载非常流畅自然。

开发指南

1、下载并安装:

ohpm install @netteam/prefetcher

有关 OpenHarmony ohpm 环境配置的更多信息,请参阅如何安装 OpenHarmony ohpm 包。

2、集成预取器:

在深入探讨细节之前,需要提及两点。

1.预取程序驱动用户代码获取所显示集合中的项目所引用的资源。换句话说,用户代码获取 URL 存储在集合项目中的图像。

2.有两种使用 Prefetcher API 的方法。第一种方法假设保存项目的数据源实现了两种附加方法:prefetch()和cancel() IDataSourcePrefetching。第二种方法(稍后介绍)使用用户注册的回调来执行相同的任务。方法的选择由用户决定,但“基于回调”的方法比“基于预取数据源”的方法更具优势:

更容易组织工作线程上的提取。

支持以数组形式呈现的集合,并因此支持Repeat组件。

通过遵循单一责任原则来更好地组织应用程序代码,因为数据源不负责执行获取。

3、使用“基于预取数据源”的方法

以下是Prefetcher 演示存储库中示例应用程序之一的详细代码片段。该应用程序演示了预取器库的使用。演示应用程序包含大量图像。每幅图像都是在应用程序内合成的彩色矩形。获取操作在主应用程序线程上执行。

import { BasicPrefetcher, IDataSourcePrefetching } from '@netteam/prefetcher';

import { image } from '@kit.ImageKit';

import { create10x10Bitmap, getRandomColor } from 'examples_common'; // Code of these functions is omitted for brevity

const ITEMS_ON_SCREEN = 8;

@Component

export struct PrefetcherDemoComponent {

  private readonly dataSource = new SimulationDataSource(2000, 500);

  private readonly prefetcher = new BasicPrefetcher(this.dataSource);

  build() {

    Column() {

      List() {

        LazyForEach(this.dataSource, (item: PictureItem) => {

          ListItem() {

            PictureItemComponent({ info: item })

              .height(`${100 / ITEMS_ON_SCREEN}%`)

          }

        }, (item: PictureItem) => item.title)

      }

      .onScrollIndex((start: number, end: number) => {

        this.prefetcher.visibleAreaChanged(start, end);

      })

    }

  }

}

@Component

export default struct PictureItemComponent {

  @ObjectLink info: PictureItem;

  build() {

    Row() {

      Image(this.info.imagePixelMap)

        .objectFit(ImageFit.Contain)

        .width('40%')

      Text(this.info.title)

        .width('60%')

    }

  }

}

@Observed

export class PictureItem {

  readonly color: number;

  title: string;

  imagePixelMap: image.PixelMap | undefined;

  key: string;

  constructor(color: number, title: string) {

    this.color = color;

    this.title = title;

    this.key = title;

  }

}

type ItemIndex = number;

type TimerId = number;

class SimulationDataSource implements IDataSourcePrefetching {

  private readonly items: PictureItem[];

  private readonly fetchDelayMs: number;

  private readonly fetches: Map<ItemIndex, TimerId> = new Map();

  constructor(numItems: number, fetchDelayMs: number) {

    this.items = [];

    this.fetchDelayMs = fetchDelayMs;

    for (let i = 0; i < numItems; i++) {

      const item = new PictureItem(getRandomColor(), `Item ${i}`);

      this.items.push(item);

    }

  }

  async prefetch(index: number): Promise<void> {

    const item = this.items[index];

    if (item.imagePixelMap) {

      return;

    }

    // Simulate long running operation

    return new Promise<void>(resolve => {

      const timeoutId = setTimeout(async () => {

        this.fetches.delete(index);

        const bitmap = create10x10Bitmap(item.color);

        const imageSource: image.ImageSource = image.createImageSource(bitmap);

        item.imagePixelMap = await imageSource.createPixelMap();

        resolve();

      }, this.fetchDelayMs);

      this.fetches.set(index, timeoutId);

    });

  }

  cancel(index: number): void {

    const timerId = this.fetches.get(index);

    if (timerId) {

      this.fetches.delete(index);

      clearTimeout(timerId);

    }

  }

  totalCount(): number {

    return this.items.length;

  }

  getData(index: number): PictureItem {

    return this.items[index];

  }

  registerDataChangeListener(_: DataChangeListener): void {}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牛马程序员2025

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值