• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 提升应用响应速度
2
3应用对用户的输入需要快速反馈,以提升交互体验,因此本文提供了以下方法来提升应用响应速度。
4
5- 避免主线程被非UI任务阻塞
6- 减少组件刷新的数量
7
8## 避免主线程被非UI任务阻塞
9
10在应用响应用户输入期间,应用主线程应尽可能只执行UI任务(待显示数据的准备、可见视图组件的更新等),非UI的耗时任务(长时间加载的内容等)建议通过异步任务延迟处理或者分配到其他线程处理。
11
12### 使用组件异步加载特性
13
14OpenHarmony提供的Image组件默认生效异步加载特性,当应用在页面上展示一批本地图片的时候,会先显示空白占位块,当图片在其他线程加载完毕后,再替换占位块。这样图片加载就可以不阻塞页面的显示,给用户带来良好的交互体验。因此,只在加载图片耗时比较短的情况下建议下述代码。
15
16```typescript
17@Entry
18@Component
19struct ImageExample1 {
20  build() {
21    Column() {
22      Row() {
23        Image('resources/base/media/sss001.jpg')
24          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
25        Image('resources/base/media/sss002.jpg')
26          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
27        Image('resources/base/media/sss003.jpg')
28          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
29        Image('resources/base/media/sss004.jpg')
30          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
31      }
32    // 此处省略若干个Row容器,每个容器内都包含如上的若干Image组件
33    }
34  }
35}
36```
37
38建议:在加载图片的耗时比较短的时候,通过异步加载的效果会大打折扣,建议配置Image的syncLoad属性。
39
40```typescript
41@Entry
42@Component
43struct ImageExample2 {
44  build() {
45    Column() {
46      Row() {
47        Image('resources/base/media/sss001.jpg')
48          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
49        Image('resources/base/media/sss002.jpg')
50          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
51        Image('resources/base/media/sss003.jpg')
52          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
53        Image('resources/base/media/sss004.jpg')
54          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
55      }
56    // 此处省略若干个Row容器,每个容器内都包含如上的若干Image组件
57    }
58  }
59}
60```
61
62### 使用TaskPool线程池异步处理
63
64OpenHarmony提供了[TaskPool线程池](../reference/apis/js-apis-taskpool.md),相比worker线程,TaskPool提供了任务优先级设置、线程池自动管理机制,示例如下:
65
66```typescript
67import taskpool from '@ohos.taskpool';
68
69@Concurrent
70function computeTask(arr: string[]): string[] {
71  // 模拟一个计算密集型任务
72  let count = 0;
73  while (count < 100000000) {
74    count++;
75  }
76  return arr.reverse();
77}
78
79@Entry
80@Component
81struct AspectRatioExample3 {
82  @State children: string[] = ['1', '2', '3', '4', '5', '6'];
83
84  aboutToAppear() {
85    this.computeTaskInTaskPool();
86  }
87
88  async computeTaskInTaskPool() {
89    const param = this.children.slice();
90    let task = new taskpool.Task(computeTask, param);
91    await taskpool.execute(task);
92  }
93
94  build() {
95    // 组件布局
96  }
97}
98```
99
100### 创建异步任务
101
102以下代码展示了将一个长时间执行的非UI任务通过Promise声明成异步任务,主线程可以先进行用户反馈-绘制初始页面。等主线程空闲时,再执行异步任务。等到异步任务运行完毕后,重绘相关组件刷新页面。
103
104```typescript
105@Entry
106@Component
107struct AspectRatioExample4 {
108  @State private children: string[] = ['1', '2', '3', '4', '5', '6'];
109  private count: number = 0;
110
111  aboutToAppear() {
112    this.computeTaskAsync();  // 调用异步运算函数
113  }
114
115  // 模拟一个计算密集型任务
116  computeTask() {
117    this.count = 0;
118    while (this.count < 100000000) {
119      this.count++;
120    }
121    this.children = this.children.reverse();
122  }
123
124  computeTaskAsync() {
125    setTimeout(() => { // 这里使用setTimeout来实现异步延迟运行
126      this.computeTask();
127    }, 1000)
128  }
129
130  build() {
131    // 组件布局
132  }
133}
134```
135
136## 减少刷新的组件数量
137
138应用刷新页面时需要尽可能减少刷新的组件数量,如果数量过多会导致主线程执行测量、布局的耗时过长,还会在自定义组件新建和销毁过程中,多次调用aboutToAppear()、aboutToDisappear()方法,增加主线程负载。
139
140### 使用容器限制刷新范围
141
142反例:如果容器内有组件被if条件包含,if条件结果变更会触发创建和销毁该组件,如果此时影响到容器的布局,该容器内所有组件都会刷新,导致主线程UI刷新耗时过长。
143
144以下代码的Text('New Page')组件被状态变量isVisible控制,isVisible为true时创建,false时销毁。当isVisible发生变化时,Stack容器内的所有组件都会刷新:
145
146```typescript
147@Entry
148@Component
149struct StackExample5 {
150  @State isVisible : boolean = false;
151
152  build() {
153    Column() {
154      Stack({alignContent: Alignment.Top}) {
155        Text().width('100%').height('70%').backgroundColor(0xd2cab3)
156          .align(Alignment.Center).textAlign(TextAlign.Center);
157
158        // 此处省略100个相同的背景Text组件
159
160        if (this.isVisible) {
161          Text('New Page').height("100%").height("70%").backgroundColor(0xd2cab3)
162            .align(Alignment.Center).textAlign(TextAlign.Center);
163        }
164      }
165      Button("press").onClick(() => {
166        this.isVisible = !(this.isVisible);
167      })
168    }
169  }
170}
171```
172
173建议:对于这种受状态变量控制的组件,在if外套一层容器,减少刷新范围。
174
175```typescript
176@Entry
177@Component
178struct StackExample6 {
179  @State isVisible : boolean = false;
180
181  build() {
182    Column() {
183      Stack({alignContent: Alignment.Top}) {
184        Text().width('100%').height('70%').backgroundColor(0xd2cab3)
185          .align(Alignment.Center).textAlign(TextAlign.Center);
186
187        // 此处省略100个相同的背景Text组件
188
189        Stack() {
190          if (this.isVisible) {
191            Text('New Page').height("100%").height("70%").backgroundColor(0xd2cab3)
192              .align(Alignment.Center).textAlign(TextAlign.Center);
193          }
194        }.width('100%').height('70%')
195      }
196      Button("press").onClick(() => {
197        this.isVisible = !(this.isVisible);
198      })
199    }
200  }
201}
202```
203
204### 按需加载列表组件的元素
205
206反例:this.arr中的每一项元素都被初始化和加载,数组中的元素有10000个,主线程执行耗时长。
207
208```typescript
209@Entry
210@Component
211struct MyComponent7 {
212  @State arr: number[] = Array.from(Array<number>(10000), (v,k) =>k);
213  build() {
214    List() {
215      ForEach(this.arr, (item: number) => {
216        ListItem() {
217          Text(`item value: ${item}`)
218        }
219      }, (item: number) => item.toString())
220    }
221  }
222}
223```
224
225建议:这种情况下用LazyForEach替换ForEach,LazyForEach一般只加载可见的元素,避免一次性初始化和加载所有元素。
226
227```typescript
228class BasicDataSource implements IDataSource {
229  private listeners: DataChangeListener[] = []
230
231  public totalCount(): number {
232    return 0
233  }
234
235  public getData(index: number): string {
236    return ''
237  }
238
239  registerDataChangeListener(listener: DataChangeListener): void {
240    if (this.listeners.indexOf(listener) < 0) {
241      console.info('add listener')
242      this.listeners.push(listener)
243    }
244  }
245
246  unregisterDataChangeListener(listener: DataChangeListener): void {
247    const pos = this.listeners.indexOf(listener);
248    if (pos >= 0) {
249      console.info('remove listener')
250      this.listeners.splice(pos, 1)
251    }
252  }
253
254  notifyDataReload(): void {
255    this.listeners.forEach(listener => {
256      listener.onDataReloaded()
257    })
258  }
259
260  notifyDataAdd(index: number): void {
261    this.listeners.forEach(listener => {
262      listener.onDataAdd(index)
263    })
264  }
265
266  notifyDataChange(index: number): void {
267    this.listeners.forEach(listener => {
268      listener.onDataChange(index)
269    })
270  }
271
272  notifyDataDelete(index: number): void {
273    this.listeners.forEach(listener => {
274      listener.onDataDelete(index)
275    })
276  }
277
278  notifyDataMove(from: number, to: number): void {
279    this.listeners.forEach(listener => {
280      listener.onDataMove(from, to)
281    })
282  }
283}
284
285class MyDataSource extends BasicDataSource {
286  private dataArray: string[] = Array.from(Array<number>(10000), (v, k) => k.toString());
287
288  public totalCount(): number {
289    return this.dataArray.length
290  }
291
292  public getData(index: number): string  {
293    return this.dataArray[index]
294  }
295
296  public addData(index: number, data: string): void {
297    this.dataArray.splice(index, 0, data)
298    this.notifyDataAdd(index)
299  }
300
301  public pushData(data: string): void {
302    this.dataArray.push(data)
303    this.notifyDataAdd(this.dataArray.length - 1)
304  }
305}
306
307@Entry
308@Component
309struct MyComponent {
310  private data: MyDataSource = new MyDataSource()
311
312  build() {
313    List() {
314      LazyForEach(this.data, (item: string) => {
315        ListItem() {
316            Text(item).fontSize(20).margin({ left: 10 })
317        }
318      }, (item:string) => item)
319    }
320  }
321}
322```