• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# @ohos.arkui.Prefetcher (Prefetching)
2Used in conjunction with **LazyForEach**, the **Prefetcher** module provides content prefetching capabilities for container components such as **List**, **Grid**, **Waterfall**, and **Swiper** during scrolling, to enhance the user browsing experience.
3
4>  **NOTE**
5>
6>  The initial APIs of this module are supported since API version 12. Updates will be marked with a superscript to indicate their earliest API version.
7
8## Modules to Import
9
10```ts
11import { BasicPrefetcher, IDataSourcePrefetching, IPrefetcher } from '@kit.ArkUI';
12```
13
14
15## IPrefetcher
16Implement this API to provide prefetching capabilities.
17
18**Atomic service API**: This API can be used in atomic services since API version 12.
19
20**System capability**: SystemCapability.ArkUI.ArkUI.Full
21
22### setDataSource
23setDataSource(dataSource: IDataSourcePrefetching): void;
24
25Sets the prefetching-capable data source to bind to the **Prefetcher** object.
26
27**Atomic service API**: This API can be used in atomic services since API version 12.
28
29**System capability**: SystemCapability.ArkUI.ArkUI.Full
30
31**Parameters**
32
33| Name       | Type                                               | Mandatory| Description        |
34|------------|---------------------------------------------------|----|------------|
35| dataSource | [IDataSourcePrefetching](#idatasourceprefetching) | Yes | Prefetching-capable data source.|
36
37```typescript
38class MyPrefetcher implements IPrefetcher {
39  private dataSource?: IDataSourcePrefetching;
40
41  setDataSource(dataSource: IDataSourcePrefetching): void {
42    this.dataSource = dataSource;
43  }
44
45  visibleAreaChanged(minVisible: number, maxVisible: number): void {
46    this.dataSource?.prefetch(minVisible);
47  }
48}
49```
50
51### visibleAreaChanged
52visibleAreaChanged(minVisible: number, maxVisible: number): void;
53
54Called when the boundaries of the visible area change. It works with the **List**, **Grid**, **Waterfall**, and **Swiper** components.
55
56**Atomic service API**: This API can be used in atomic services since API version 12.
57
58**System capability**: SystemCapability.ArkUI.ArkUI.Full
59
60**Parameters**
61
62| Name       | Type    | Mandatory| Description       |
63|------------|--------|----|-----------|
64| minVisible | number | Yes | Upper bound of the visible area.|
65| maxVisible | number | Yes | Lower bound of the visible area.|
66
67```typescript
68class MyPrefetcher implements IPrefetcher {
69  private dataSource?: IDataSourcePrefetching;
70
71  setDataSource(dataSource: IDataSourcePrefetching): void {
72    this.dataSource = dataSource;
73  }
74
75  visibleAreaChanged(minVisible: number, maxVisible: number): void {
76    this.dataSource?.prefetch(minVisible);
77  }
78}
79```
80
81## BasicPrefetcher
82As a fundamental implementation of **IPrefetcher**, offers an intelligent data prefetching algorithm that decides which data items to prefetch based on real-time changes in the visible area on the screen and variations in the prefetch duration. It can also determine which prefetch requests should be canceled based on the user's scrolling actions.
83
84**Atomic service API**: This API can be used in atomic services since API version 12.
85
86**System capability**: SystemCapability.ArkUI.ArkUI.Full
87
88### constructor
89constructor(dataSource?: IDataSourcePrefetching);
90
91A constructor used to create a **DataSource** instance.
92
93**Atomic service API**: This API can be used in atomic services since API version 12.
94
95**System capability**: SystemCapability.ArkUI.ArkUI.Full
96
97**Parameters**
98
99| Name       | Type                                               | Mandatory| Description        |
100|------------|---------------------------------------------------|----|------------|
101| dataSource | [IDataSourcePrefetching](#idatasourceprefetching) | No | Prefetching-capable data source.|
102
103### setDataSource
104setDataSource(dataSource: IDataSourcePrefetching): void;
105
106Sets the prefetching-capable data source to bind to the **Prefetcher** object.
107
108**Atomic service API**: This API can be used in atomic services since API version 12.
109
110**System capability**: SystemCapability.ArkUI.ArkUI.Full
111
112**Parameters**
113
114| Name       | Type                                               | Mandatory| Description        |
115|------------|---------------------------------------------------|----|------------|
116| dataSource | [IDataSourcePrefetching](#idatasourceprefetching) | Yes | Prefetching-capable data source.|
117
118### visibleAreaChanged
119visibleAreaChanged(minVisible: number, maxVisible: number): void;
120
121Called when the boundaries of the visible area change. It works with the **List**, **Grid**, **Waterfall**, and **Swiper** components.
122
123**Atomic service API**: This API can be used in atomic services since API version 12.
124
125**System capability**: SystemCapability.ArkUI.ArkUI.Full
126
127**Parameters**
128
129| Name       | Type    | Mandatory| Description       |
130|------------|--------|----|-----------|
131| minVisible | number | Yes | Upper bound of the visible area.|
132| maxVisible | number | Yes | Lower bound of the visible area.|
133
134## IDataSourcePrefetching
135
136Extends the [IDataSource](./arkui-ts/ts-rendering-control-lazyforeach.md#idatasource10) API to provide a data source with prefetching capabilities.
137
138**Atomic service API**: This API can be used in atomic services since API version 12.
139
140**System capability**: SystemCapability.ArkUI.ArkUI.Full
141
142### prefetch
143prefetch(index: number): Promise\<void\> | void;
144
145Prefetches a specified data item from the dataset. This API can be either synchronous or asynchronous.
146
147**Atomic service API**: This API can be used in atomic services since API version 12.
148
149**System capability**: SystemCapability.ArkUI.ArkUI.Full
150
151**Parameters**
152
153| Name  | Type    | Mandatory| Description      |
154|-------|--------|----|----------|
155| index | number | Yes | Index of the data item to prefetch.|
156
157### cancel
158cancel?(index: number): Promise\<void\> | void;
159
160Cancels the prefetching of a specified data item from the dataset. This API can be either synchronous or asynchronous.
161
162**Atomic service API**: This API can be used in atomic services since API version 12.
163
164**System capability**: SystemCapability.ArkUI.ArkUI.Full
165
166**Parameters**
167
168| Name  | Type    | Mandatory| Description        |
169|-------|--------|----|------------|
170| index | number | Yes | Index of the data item to cancel prefetching for.|
171
172When list content moves off the screen (for example, during fast scrolling scenarios), once the prefetching algorithm determines which items outside the screen can have their prefetching canceled, this API is called. For instance, if the HTTP framework supports request cancellation, network requests initiated in the **prefetch** API can be canceled here.
173
174## Example
175
176This example demonstrates the prefetching capabilities of **Prefetcher**. It uses pagination with **LazyForEach** to achieve lazy loading effects and simulates the loading process with delays.
177
178```typescript
179import { BasicPrefetcher, IDataSourcePrefetching } from '@kit.ArkUI';
180import { image } from '@kit.ImageKit';
181
182const ITEMS_ON_SCREEN = 8;
183
184@Entry
185@Component
186struct PrefetcherDemoComponent {
187  private page: number = 1;
188  private pageSize: number = 50;
189  private breakPoint: number = 25;
190  private readonly fetchDelayMs: number = 500;
191  private readonly dataSource = new MyDataSource(this.page, this.pageSize, this.fetchDelayMs);
192  private readonly prefetcher = new BasicPrefetcher(this.dataSource);
193
194  build() {
195    Column() {
196      List() {
197        LazyForEach(this.dataSource, (item: PictureItem, index: number) => {
198          ListItem() {
199            PictureItemComponent({ info: item })
200              .height(`${100 / ITEMS_ON_SCREEN}%`)
201          }
202          .onAppear(() => {
203            if (index >= this.breakPoint) {
204              this.dataSource.getHttpData(++this.page, this.pageSize);
205              this.breakPoint = this.dataSource.totalCount() - this.pageSize / 2;
206            }
207          })
208        }, (item: PictureItem) => item.title)
209      }
210      .onScrollIndex((start: number, end: number) => {
211        this.prefetcher.visibleAreaChanged(start, end);
212      })
213    }
214  }
215}
216
217@Component
218struct PictureItemComponent {
219  @ObjectLink info: PictureItem;
220
221  build() {
222    Row() {
223      Image(this.info.imagePixelMap)
224        .objectFit(ImageFit.Contain)
225        .width('40%')
226      Text(this.info.title)
227        .width('60%')
228    }
229  }
230}
231
232@Observed
233class PictureItem {
234  readonly color: number;
235  title: string;
236  imagePixelMap: image.PixelMap | undefined;
237  key: string;
238
239  constructor(color: number, title: string) {
240    this.color = color;
241    this.title = title;
242    this.key = title;
243  }
244}
245
246type ItemIndex = number;
247type TimerId = number;
248
249class MyDataSource implements IDataSourcePrefetching {
250  private readonly items: PictureItem[];
251  private readonly fetchDelayMs: number;
252  private readonly fetches: Map<ItemIndex, TimerId> = new Map();
253  private readonly listeners: DataChangeListener[] = [];
254
255  constructor(pageNum: number, pageSize: number, fetchDelayMs: number) {
256    this.items = [];
257    this.fetchDelayMs = fetchDelayMs;
258    this.getHttpData(pageNum, pageSize);
259  }
260
261  async prefetch(index: number): Promise<void> {
262    const item = this.items[index];
263    if (item.imagePixelMap) {
264      return;
265    }
266
267    // Perform time-consuming operations.
268    return new Promise<void>(resolve => {
269      const timeoutId = setTimeout(async () => {
270        this.fetches.delete(index);
271        const bitmap = create10x10Bitmap(item.color);
272        const imageSource: image.ImageSource = image.createImageSource(bitmap);
273        item.imagePixelMap = await imageSource.createPixelMap();
274        resolve();
275      }, this.fetchDelayMs);
276
277      this.fetches.set(index, timeoutId)
278    });
279  }
280
281  cancel(index: number): void {
282    const timerId = this.fetches.get(index);
283    if (timerId) {
284      this.fetches.delete(index);
285      clearTimeout(timerId);
286    }
287  }
288
289  // Simulate paginated data loading.
290  getHttpData(pageNum: number, pageSize:number): void {
291    const newItems: PictureItem[] = [];
292    for (let i = (pageNum - 1) * pageSize; i < pageNum * pageSize; i++) {
293      const item = new PictureItem(getRandomColor(), `Item ${i}`);
294      newItems.push(item);
295    }
296    const startIndex = this.items.length;
297    this.items.splice(startIndex, 0, ...newItems);
298    this.notifyBatchUpdate([
299      {
300        type: DataOperationType.ADD,
301        index: startIndex,
302        count: newItems.length,
303        key: newItems.map((item) => item.title)
304      }
305    ]);
306  }
307
308  private notifyBatchUpdate(operations: DataOperation[]) {
309    this.listeners.forEach((listener: DataChangeListener) => {
310      listener.onDatasetChange(operations);
311    });
312  }
313
314  totalCount(): number {
315    return this.items.length;
316  }
317
318  getData(index: number): PictureItem {
319    return this.items[index];
320  }
321
322  registerDataChangeListener(listener: DataChangeListener): void {
323    if (this.listeners.indexOf(listener) < 0) {
324      this.listeners.push(listener);
325    }
326  }
327
328  unregisterDataChangeListener(listener: DataChangeListener): void {
329    const pos = this.listeners.indexOf(listener);
330    if (pos >= 0) {
331      this.listeners.splice(pos, 1);
332    }
333  }
334}
335
336function getRandomColor(): number {
337  const maxColorCode = 256;
338  const r = Math.floor(Math.random() * maxColorCode);
339  const g = Math.floor(Math.random() * maxColorCode);
340  const b = Math.floor(Math.random() * maxColorCode);
341
342  return (r * 256 + g) * 256 + b;
343}
344
345function create10x10Bitmap(color: number): ArrayBuffer {
346  const height = 10;
347  const width = 10;
348
349  const fileHeaderLength = 14;
350  const bitmapInfoLength = 40;
351  const headerLength = fileHeaderLength + bitmapInfoLength;
352  const pixelSize = (width * 3 + 2) * height;
353
354  let length = pixelSize + headerLength;
355
356  const buffer = new ArrayBuffer(length);
357  const view16 = new Uint16Array(buffer);
358
359  view16[0] = 0x4D42;
360  view16[1] = length & 0xffff;
361  view16[2] = length >> 16;
362  view16[5] = headerLength;
363
364  let offset = 7;
365  view16[offset++] = bitmapInfoLength & 0xffff;
366  view16[offset++] = bitmapInfoLength >> 16;
367  view16[offset++] = width & 0xffff;
368  view16[offset++] = width >> 16;
369  view16[offset++] = height & 0xffff;
370  view16[offset++] = height >> 16;
371  view16[offset++] = 1;
372  view16[offset++] = 24;
373
374  const b = color & 0xff;
375  const g = (color >> 8) & 0xff;
376  const r = color >> 16;
377  offset = headerLength;
378  const view8 = new Uint8Array(buffer);
379  for (let y = 0; y < height; y++) {
380    for (let x = 0; x < width; x++) {
381      view8[offset++] = b;
382      view8[offset++] = g;
383      view8[offset++] = r;
384    }
385    offset += 2;
386  }
387
388  return buffer;
389}
390```
391
392The following figure illustrates the effects.
393
394![Prefetcher-Demo](./figures/prefetcher-demo.gif)
395
396## Supplementary Notes
397
398You can also use the OpenHarmony third-party library [@netteam/prefetcher](https://ohpm.openharmony.cn/#/en/detail/@netteam%2Fprefetcher) to implement the prefetching functionality. This library provides additional APIs for more convenient and efficient data prefetching.
399