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 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