性能优化场景
1.应用冷启动优化
应用冷启动概述
应用启动可以分为冷启动和热启动,当应用启动时,后台没有该应用的进程,这时系统会重新创建应用的进程, 这种启动方式就叫做冷启动 ;而热启动是当应用程序已经在后台运行,用户再次打开应用程序时,应用程序仍然在内存中,可以直接从内存中加载并继续之前的状态,而不需要重新初始化和加载资源。
减少主线程非UI耗时操作
在应用启动流程中,主要聚焦在执行UI相关操作中,为了更快的显示首页内容,不建议在主线程中执行非UI相关的耗时操作,建议通过异步任务进行延迟处理或放到其他子线程中执行,
缩短加载绘制首页阶段耗时
首页加载绘制阶段主要包含加载首页内容、测量布局、刷新组件并绘制。同样注意页面生命周期的处理函数,不要进行耗时操作,同时,应优先创建首页需要显示的组件,使用if分支语句,隐藏不需要显示的组件,减少创建过程的耗时。耗时操作建议通过异步任务延迟处理或者放到其他线程执行,线程并发方案可以参考:TaskPool和Worker的对比实践。
2.长列表加载性能优化
动态预加载
LazyForEach懒加载可以通过使用Prefetcher来预取和预渲染数据。在使用Prefetcher后,除屏幕内显示的ListItem组件外,还会预先将屏幕可视区外的部分列表项数据进行预渲染和预取。这样当列表向下滑动时,会先显示预渲染组件,屏幕可视区外会动态调整预取范围。
布局优化
可以对ListItem进行布局优化,把线性布局修改为相对布局,就可以将最大嵌套层级从5层降低到2层。
3.应用包大小优化
配置so压缩选型
当前DevEco Studio默认打包应用时不压缩so库文件,配置so压缩选项后,DevEco Studio会将so库文件以压缩形式打包到包中,从而减小应用包大小。
多包场景下使用HSP共享代码和资源
当前系统提供了两种共享包,HAR静态共享包和HSP动态共享包。最大的不同之处在于:HAR中的代码和资源跟随使用方编译,如果有多个使用方,它们的编译产物中会存在多份相同拷贝;而HSP中的代码和资源可以独立编译,在运行时进程中代码和资源也只会存在一份。
在多包场景下,如果应用的多个HAP或HSP包使用HAR包实现代码和资源的共享,那么打包后的每个HAP或HSP包中都会存在一份共享HAR包的拷贝,导致App包中存在冗余代码和资源。如下图示例,应用模块HAP1和HAP2/HSP1都引用了HAR2和HAR3,打包后,App包中HAR2和HAR3存在多份重复拷贝,体积较大。
推荐开发者使用HSP代替HAR实现代码和资源共享。
4.文件上传下载性能提升
断点续传
断点续传功能的实现,不管是应用端还是服务器端都需要用到合理的技术来互相协同。在实际开发中,开发者无需亲自实现断点续传功能,只需对SDK进行合理配置。
文件上传
对于大文件断点续传上传,本文采用request(上传下载)模块中的request.agent任务托管接口,可以自动实现暂停继续重试等操作,无需手动将文件分片和记录上传分片信息
TypeScript
private config: request.agent.Config = {
action: request.agent.Action.UPLOAD,
headers: HEADER,
url: '',
mode: request.agent.Mode.BACKGROUND,
method: 'POST',
title: 'upload',
network: request.agent.Network.ANY,
data: [],
token: 'UPLOAD_TOKEN'
}
...
// 转换uri
private async getFilesAndData(cacheDir: string, fileUris: Array<string>): Promise<Array<request.agent.FormItem>> {
...
}
// 创建文件上传后台任务
async createBackgroundTask(fileUris: Array<string>) {
if (this.context === undefined) {
return;
}
// 获取上传url
this.config.url = await urlUtils.getUrl(this.context);
this.config.data = await this.getFilesAndData(this.context.cacheDir, fileUris);
this.config.mode = request.agent.Mode.BACKGROUND;
try {
this.backgroundTask = await request.agent.create(this.context, this.config);
await this.backgroundTask.start();
let state = AppStorage.get<number>('backTaskState');
if (state === BackgroundTaskState.PAUSE) {
await this.backgroundTask.pause();
}
logger.info(TAG, `createBackgroundTask success`);
} catch (err) {
logger.error(TAG, `task err, err = ${JSON.stringify(err)}`);
}
}