From 27d321b7ba2b54a43555d4e4b89da0d6dfddb2c4 Mon Sep 17 00:00:00 2001 From: liugang9704 <2745340733@qq.com> Date: Mon, 29 Sep 2025 14:43:54 +0800 Subject: [PATCH] =?UTF-8?q?update:=20=E5=90=8C=E6=BA=90=E5=B7=A5=E7=A8=8B?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AvoidTimeComsume/README.md | 35 ++++++++++--------- .../main/ets/entryability/EntryAbility.ets | 6 +++- .../entry/src/main/ets/pages/Index.ets | 12 ++++++- .../main/ets/views/ConditionalRendering.ets | 2 +- .../entry/src/main/ets/views/GetStrOfId.ets | 8 +++-- .../src/main/ets/views/GetStrOfResource.ets | 8 +++-- .../src/main/ets/views/PositiveOfProperty.ets | 5 ++- .../entry/src/main/ets/views/UseAsync.ets | 15 +++++--- .../entry/src/main/ets/views/UseTaskPool.ets | 27 ++++++++++---- 9 files changed, 83 insertions(+), 35 deletions(-) diff --git a/AvoidTimeComsume/README.md b/AvoidTimeComsume/README.md index f95b9a04..1d65bbcc 100644 --- a/AvoidTimeComsume/README.md +++ b/AvoidTimeComsume/README.md @@ -1,7 +1,7 @@ # **主线程耗时操作优化指导** ## 介绍 在应用开发实践中,有效避免主线程执行冗余与易耗时操作是至关重要的策略。此举能有效降低主线程负载,提升UI的响应速度。面对高频回调接口在短时间内密集触发的场景,需要避免接口内的耗时操作,尽量保证主线程不被长时间占用,从而防止阻塞UI渲染,引发界面卡顿或掉帧现象。 -本实例结束开发过程中常见的冗余操作,常见的高频回调场景以及其他主线程优化思路。 +本实例介绍开发过程中常见的冗余操作,常见的高频回调场景以及其他主线程优化思路。 ## 预览效果 ![](screenshots/output-15_53_54.gif) @@ -16,7 +16,7 @@ │ │ └──EntryBackupAbility.ets │ ├──pages │ │ └──Index.ets // 首页 -│ └──views.ets +│ └──views │ ├──ConditionalRendering.ets // 条件渲染页 │ ├──GetStrOfId.ets // ID资源获取页 │ ├──GetStrOfResource.ets // 资源获取页 @@ -36,22 +36,23 @@ └──entry/src/main/resources // 应用资源目录 ``` ## 使用说明 -“避免冗余操作”的正例和反例,反例中包含release版本中冗余日志打印、Trace打点以及无业务代码的空回调,降低了性能。 - -“高频回调场景中避免执行耗时操作”的正例和反例,以下常见高频回调场景中需要避免执行耗时操作: -* 高频事件回调 -* 组件复用回调 -* 组件生命周期回调 -* 循环渲染 -* 组件属性 - -反例中在高频事件中执行了上述耗时操作,降低了性能。 - -“避免使用耗时接口”的正例和反例,反例中使用了没有指定资源id的接口,导致耗时增加,从而影响了性能。 - -“使用多线程”是正例,开启多线程后,渲染页面速度加快,提升了性能。 +* 点击含有冗余操作按钮,进入二级页面。 +* 点击不含有冗余操作按钮,进入二级页面。 +* 点击组件复用反例按钮,进入二级页面,上下滑动,观察页面存在掉帧的现象。 +* 点击组件复用正例按钮,进入二级页面,上下滑动,页面滑动流畅。 +* 点击循环渲染反例按钮,进入二级页面,上下滑动,观察页面存在掉帧的现象。 +* 点击循环渲染正例按钮,进入二级页面,上下滑动,页面滑动流畅。 +* 点击组件属性反例按钮,进入二级页面,点击按钮,观察页面变化。 +* 点击组件属性正例按钮,进入二级页面,点击按钮,观察页面变化。 +* 点击通过资源对象按钮,进入二级页面。 +* 点击通过资源ID按钮,进入二级页面。 +* 点击使用异步按钮,进入二级页面,上下滑动,页面滑动流畅。 +* 点击使用多线程按钮,进入二级页面,上下滑动,页面滑动流畅。 +* 点击高频事件回调执行耗时操作按钮,进入二级页面。 +* 点击高频事件回调不执行耗时操作按钮,进入二级页面。 +* 点击条件渲染按钮,进入二级页面,点击按钮,观察页面变化。 ## 相关权限 -无 +不涉及 ## 约束与限制 * 本示例仅支持标准系统上运行,支持设备:华为手机。 diff --git a/AvoidTimeComsume/entry/src/main/ets/entryability/EntryAbility.ets b/AvoidTimeComsume/entry/src/main/ets/entryability/EntryAbility.ets index 80c495ed..471cad2a 100644 --- a/AvoidTimeComsume/entry/src/main/ets/entryability/EntryAbility.ets +++ b/AvoidTimeComsume/entry/src/main/ets/entryability/EntryAbility.ets @@ -39,7 +39,11 @@ export default class EntryAbility extends UIAbility { return; } hilog.info(0x0000, 'Sample', 'Succeeded in loading the content.'); - AppStorage.setOrCreate('uiContext', windowStage.getMainWindowSync().getUIContext()); + try { + AppStorage.setOrCreate('uiContext', windowStage.getMainWindowSync().getUIContext()); + } catch (error) { + hilog.error(0x0000, 'Sample', 'Failed to getMainWindowSync. Cause: %{public}s', JSON.stringify(error) ?? ''); + } }); windowStage.getMainWindow().then((windowObj) => { diff --git a/AvoidTimeComsume/entry/src/main/ets/pages/Index.ets b/AvoidTimeComsume/entry/src/main/ets/pages/Index.ets index e0258427..8e2e7bf6 100644 --- a/AvoidTimeComsume/entry/src/main/ets/pages/Index.ets +++ b/AvoidTimeComsume/entry/src/main/ets/pages/Index.ets @@ -12,6 +12,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { BusinessError } from '@kit.BasicServicesKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; class DirectoryItem { title: ResourceStr = ''; @@ -107,6 +109,8 @@ struct Index { .onClick(() => { this.getUIContext().getRouter().pushUrl({ url: item.uri + }).catch((error: BusinessError)=>{ + hilog.error(0x0000,'',`pushUrl failed. code=${error.code}, message=${error.message}`); }); }) } @@ -128,6 +132,8 @@ struct Index { .onClick(() => { this.getUIContext().getRouter().pushUrl({ url: 'views/NegativeOfOnScroll' + }).catch((error: BusinessError)=>{ + hilog.error(0x0000,'',`pushUrl failed. code=${error.code}, message=${error.message}`); }); }) Button($r('app.string.positive_of_onScroll')) @@ -139,6 +145,8 @@ struct Index { .onClick(() => { this.getUIContext().getRouter().pushUrl({ url: 'views/PositiveOfOnScroll' + }).catch((error: BusinessError)=>{ + hilog.error(0x0000,'',`pushUrl failed. code=${error.code}, message=${error.message}`); }); }) Button($r('app.string.conditional_rendering')) @@ -150,7 +158,9 @@ struct Index { .onClick(() => { this.getUIContext().getRouter().pushUrl({ url: 'views/ConditionalRendering' - }); + }).catch((error: BusinessError)=>{ + hilog.error(0x0000,'',`pushUrl failed. code=${error.code}, message=${error.message}`); + });; }) } .padding({ left: 16, right: 16, bottom: 44 }) diff --git a/AvoidTimeComsume/entry/src/main/ets/views/ConditionalRendering.ets b/AvoidTimeComsume/entry/src/main/ets/views/ConditionalRendering.ets index 5129593e..f3f2f2ea 100644 --- a/AvoidTimeComsume/entry/src/main/ets/views/ConditionalRendering.ets +++ b/AvoidTimeComsume/entry/src/main/ets/views/ConditionalRendering.ets @@ -78,7 +78,7 @@ struct MyComponent { @Component struct CustomComponentA { aboutToAppear(): void { - let temp = 0; + let temp: number = 0; for (let i = 0; i < 1000000; i++) { temp += 1; } diff --git a/AvoidTimeComsume/entry/src/main/ets/views/GetStrOfId.ets b/AvoidTimeComsume/entry/src/main/ets/views/GetStrOfId.ets index c0c91c35..cc0c6b41 100644 --- a/AvoidTimeComsume/entry/src/main/ets/views/GetStrOfId.ets +++ b/AvoidTimeComsume/entry/src/main/ets/views/GetStrOfId.ets @@ -14,7 +14,7 @@ */ // [Start get_str_of_id] -import { hiTraceMeter } from '@kit.PerformanceAnalysisKit'; +import { hilog, hiTraceMeter } from '@kit.PerformanceAnalysisKit'; @Entry @Component @@ -24,7 +24,11 @@ struct GetStrOfId { aboutToAppear(): void { hiTraceMeter.startTrace('getStringSyncAfter', 1); // The input parameter of the getStringSync operation directly uses the resource and does use the resource ID - this.getUIContext().getHostContext()!.resourceManager.getStringSync($r('app.string.test').id) + try { + this.getUIContext().getHostContext()!.resourceManager.getStringSync($r('app.string.test').id) + } catch (error) { + hilog.error(0x0000,'',`getStringSync failed. code=${error.code}, message=${error.message}`); + } hiTraceMeter.finishTrace('getStringSyncAfter', 1) } diff --git a/AvoidTimeComsume/entry/src/main/ets/views/GetStrOfResource.ets b/AvoidTimeComsume/entry/src/main/ets/views/GetStrOfResource.ets index db9f6697..edf6f5ec 100644 --- a/AvoidTimeComsume/entry/src/main/ets/views/GetStrOfResource.ets +++ b/AvoidTimeComsume/entry/src/main/ets/views/GetStrOfResource.ets @@ -14,7 +14,7 @@ */ // [Start get_str_of_resource] -import { hiTraceMeter } from '@kit.PerformanceAnalysisKit'; +import { hilog, hiTraceMeter } from '@kit.PerformanceAnalysisKit'; @Entry @@ -25,7 +25,11 @@ struct GetStrOfResource { aboutToAppear(): void { hiTraceMeter.startTrace('getStringSync', 1); // The input parameter of the getStringSync operation directly uses the resource and does not use the resource ID - this.getUIContext().getHostContext()!.resourceManager.getStringSync($r('app.string.test')) + try { + this.getUIContext().getHostContext()!.resourceManager.getStringSync($r('app.string.test').id) + } catch (error) { + hilog.error(0x0000,'',`getStringSync failed. code=${error.code}, message=${error.message}`); + } hiTraceMeter.finishTrace('getStringSync', 1) } diff --git a/AvoidTimeComsume/entry/src/main/ets/views/PositiveOfProperty.ets b/AvoidTimeComsume/entry/src/main/ets/views/PositiveOfProperty.ets index 1ce7eab8..431f46ec 100644 --- a/AvoidTimeComsume/entry/src/main/ets/views/PositiveOfProperty.ets +++ b/AvoidTimeComsume/entry/src/main/ets/views/PositiveOfProperty.ets @@ -16,6 +16,7 @@ // [Start positive_of_property] import { taskpool } from '@kit.ArkTS'; // Task pools import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BusinessError } from '@kit.BasicServicesKit'; @Concurrent function getHeight(): number { @@ -31,7 +32,9 @@ function getHeight(): number { // Execute getHeight taskpool.execute(getHeight).then((value: Object) => { AppStorage.setOrCreate('height', value); -}) +}).catch((error: BusinessError)=>{ + hilog.error(0x0000,'',`execute failed. code=${error.code}, message=${error.message}`); +}); @Entry @Component diff --git a/AvoidTimeComsume/entry/src/main/ets/views/UseAsync.ets b/AvoidTimeComsume/entry/src/main/ets/views/UseAsync.ets index cb40596a..76f17e27 100644 --- a/AvoidTimeComsume/entry/src/main/ets/views/UseAsync.ets +++ b/AvoidTimeComsume/entry/src/main/ets/views/UseAsync.ets @@ -16,7 +16,8 @@ import { Item, ResponseData, dataToItem } from '../common/Item'; import { WaterFlowDataSource } from './WaterFlowDataSource'; import { buffer } from '@kit.ArkTS'; - +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BusinessError } from '@kit.BasicServicesKit'; @Entry @Component @@ -122,9 +123,15 @@ struct WaterFlowExample2 { // data.json is the local json data, which is about 20 MB in size, and simulates getting data from the network await this.getUIContext().getHostContext()!.resourceManager.getRawFileContent('data.json').then((data: Uint8Array) => { // parse json - let str = buffer.from(data).toString(); - res = JSON.parse(str); - }) + try { + let str: string = buffer.from(data).toString(); + res = JSON.parse(str); + } catch (error) { + hilog.error(0x0000,'',`buffer.from failed. code=${error.code}, message=${error.message}`); + } + }).catch((error: BusinessError)=>{ + hilog.error(0x0000,'',`getRawFileContent failed. code=${error.code}, message=${error.message}`); + }); return dataToItem(res.data) } diff --git a/AvoidTimeComsume/entry/src/main/ets/views/UseTaskPool.ets b/AvoidTimeComsume/entry/src/main/ets/views/UseTaskPool.ets index cd2ecf9f..650c4dde 100644 --- a/AvoidTimeComsume/entry/src/main/ets/views/UseTaskPool.ets +++ b/AvoidTimeComsume/entry/src/main/ets/views/UseTaskPool.ets @@ -16,6 +16,8 @@ import { buffer, taskpool } from '@kit.ArkTS'; import { Item, ResponseData, dataToItem } from '../common/Item'; import { WaterFlowDataSource } from './WaterFlowDataSource'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BusinessError } from '@kit.BasicServicesKit'; @Entry @Component @@ -23,7 +25,7 @@ struct WaterFlowExample { @State fontSize: number = 24; scroller: Scroller = new Scroller(); dataSource: WaterFlowDataSource = new WaterFlowDataSource(); - UIContext = this.getUIContext() + UIContext: UIContext = this.getUIContext() @Builder itemFoot() { @@ -123,18 +125,31 @@ struct WaterFlowExample { // The following methods are defined outside of the component async function taskPoolExecute(UIContext: UIContext): Promise { let task: taskpool.Task = new taskpool.Task(mockRequestData, UIContext); - return await taskpool.execute(task) as Item[]; + let result: Item[] = []; + try { + result = await taskpool.execute(task) as Item[] + } catch (error) { + hilog.error(0x0000,'',`execute failed. code=${error.code}, message=${error.message}`); + } + return result; } +@Concurrent async function mockRequestData(context: Context): Promise { let res: ResponseData = new ResponseData(); // data.json is the local json data, which is about 20 MB in size, and simulates getting data from the network await context.resourceManager.getRawFileContent('data.json').then((data: Uint8Array) => { // parse json - let str = buffer.from(data).toString(); - res = JSON.parse(str); - }) - let arr = dataToItem(res.data); + try { + let str: string = buffer.from(data).toString(); + res = JSON.parse(str); + } catch (error) { + hilog.error(0x0000,'',`buffer.from failed. code=${error.code}, message=${error.message}`); + } + }).catch((error: BusinessError)=>{ + hilog.error(0x0000,'',`getRawFileContent failed. code=${error.code}, message=${error.message}`); + }); + let arr: Item[] = dataToItem(res.data); return arr; } -- Gitee