• Home
Name Date Size #Lines LOC

..--

AppScope/22-Oct-2025-4239

entry/22-Oct-2025-1,9781,600

figures/22-Oct-2025-

hvigor/22-Oct-2025-3836

.gitignoreD22-Oct-2025133 1212

README.mdD22-Oct-20258.4 KiB225176

build-profile.json5D22-Oct-20251.4 KiB5857

code-linter.json5D22-Oct-20251.4 KiB4746

hvigorfile.tsD22-Oct-2025836 215

oh-package.json5D22-Oct-2025834 2624

README.md

1# 使用NDK多线程创建UI组件
2
3### 介绍
4
5本示例介绍如何使用多线程Native接口在非UI线程创建UI组件,从而优化组件创建耗时和响应时延。
6
7### 效果图预览
8
9![build_on_multi_thread](figures/build_on_multi_thread.gif)
10
11**使用说明**
12
131. 点击CreatePageOnMultiThread按钮,多线程创建UI页面;
142. 点击CreatePageOnUIThread按钮,在UI线程创建UI页面作为对比。
15
16### 实现思路
17
18点击CreatePageOnMultiThread按钮,跳转到多线程创建的UI页面,页面内的UI组件在多线程并行创建。
19
201. CAPIComponent自定义组件用于挂载通过Native接口创建的组件树。源码参考[Page.ets](./entry/src/main/ets/pages/Page.ets),根据isOnUIThread的状态分别调用CreateNodeTreeOnUIThread在UI线程创建组件和CreateNodeTreeOnMultiThread在多线程创建组件。
21
22```ts
23import { NodeContent, router } from '@kit.ArkUI';
24import entry from 'libentry.so';
25
26@Component
27struct CAPIComponent {
28  private rootSlot = new NodeContent();
29  @State isOnUIThread: boolean = false;
30
31  aboutToAppear(): void {
32    if (this.isOnUIThread) {
33      // 调用Native接口在UI线程创建组件。
34      entry.CreateNodeTreeOnUIThread(this.rootSlot, this.getUIContext());
35    } else {
36      // 调用Native接口多线程创建组件。
37      entry.CreateNodeTreeOnMultiThread(this.rootSlot, this.getUIContext());
38    }
39  }
40
41  aboutToDisappear(): void {
42    // 释放已创建的Native组件。
43    entry.DisposeNodeTree(this.rootSlot);
44  }
45
46  build() {
47    Column() {
48      // Native组件树挂载点。
49      ContentSlot(this.rootSlot)
50    }
51    .width('100%')
52  }
53}
54```
55
562. CreateNodeTreeOnMultiThread是对ArkTs侧暴露的native接口,此接口负责多线程创建UI组件。示例中把页面中的每个卡片拆分为一个子任务,调用OH_ArkUI_PostAsyncUITask接口在非UI线程创建卡片对应的UI组件树。源码参考[NodeCreator.cpp](./entry/src/main/cpp/node/NodeCreator.cpp)
57
58```cpp
59napi_value CreateNodeTreeOnMultiThread(napi_env env, napi_callback_info info) {
60    size_t argc = 2;
61    napi_value args[2] = { nullptr, nullptr };
62    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
63
64    // 获取ArkTs侧组件挂载点。
65    ArkUI_NodeContentHandle contentHandle;
66    int32_t result = OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
67    if (result != ARKUI_ERROR_CODE_NO_ERROR) {
68        OH_LOG_ERROR(LOG_APP, "OH_ArkUI_GetNodeContentFromNapiValue Failed %{public}d", result);
69        return nullptr;
70    }
71
72    // 获取上下文对象指针。
73    ArkUI_ContextHandle contextHandle;
74    result = OH_ArkUI_GetContextFromNapiValue(env, args[1], &contextHandle);
75    if (result != ARKUI_ERROR_CODE_NO_ERROR) {
76        OH_LOG_ERROR(LOG_APP, "OH_ArkUI_GetContextFromNapiValue Failed %{public}d", result);
77        delete contextHandle;
78        return nullptr;
79    }
80
81    // 创建native侧组件树根节点。
82    auto scrollNode = std::make_shared<ArkUIScrollNode>();
83    scrollNode->SetScrollBarDisplayMode(ARKUI_SCROLL_BAR_DISPLAY_MODE_OFF);
84
85    // 将native侧组件树根节点挂载到UI主树上。
86    result = OH_ArkUI_NodeContent_AddNode(contentHandle, scrollNode->GetHandle());
87    if (result != ARKUI_ERROR_CODE_NO_ERROR) {
88        OH_LOG_ERROR(LOG_APP, "OH_ArkUI_NodeContent_AddNode Failed %{public}d", result);
89        delete contextHandle;
90        return nullptr;
91    }
92    // 保存native侧组件树。
93    g_nodeMap[contentHandle] = scrollNode;
94
95    auto columnNode = std::make_shared<ArkUIColumnNode>();
96    scrollNode->AddChild(columnNode);
97
98    for (int32_t i=0;i<g_cardTypeInfos.size();i++) {
99        // UI线程创建子树根节点,保证scroll的子节点顺序。
100        auto columnItem = std::make_shared<ArkUIColumnNode>();
101        columnItem->SetMargin(NODE_MARGIN, 0, NODE_MARGIN, 0);
102        columnNode->AddChild(columnItem);
103        AsyncData* asyncData = new AsyncData();
104        asyncData->parent = columnItem;
105        asyncData->cardInfo = g_cardTypeInfos[i];
106        // 在非UI线程创建组件树,创建完成后回到主线程挂载到UI主树上。
107        result = OH_ArkUI_PostAsyncUITask(contextHandle, asyncData, CreateCardNodeTree, MountNodeTree);
108        if (result != ARKUI_ERROR_CODE_NO_ERROR) {
109            OH_LOG_ERROR(LOG_APP, "OH_ArkUI_PostAsyncUITask Failed %{public}d", result);
110            delete asyncData;
111        }
112    }
113    delete contextHandle;
114    return nullptr;
115}
116```
117
1183. CreateCardNodeTree会在非UI线程被调用,根据卡片类型创建对应的UI组件树并设置属性。源码参考[NodeCreator.cpp](./entry/src/main/cpp/node/NodeCreator.cpp)
119
120```cpp
121void CreateCardNodeTree(void *asyncUITaskData) {
122    auto asyncData = static_cast<AsyncData*>(asyncUITaskData);
123    if (!asyncData) {
124        return;
125    }
126
127    if (asyncData->cardInfo.type == "App") {
128        AppCardInfo info = asyncData->cardInfo.appCardInfo;
129        asyncData->child = CreateAppCard(info);
130    } else if (asyncData->cardInfo.type == "Service") {
131        ServiceCardInfo info = asyncData->cardInfo.serviceCardInfo;
132        asyncData->child = CreateServiceCard(info);
133    }
134}
135```
136
1374. CreateCardNodeTree执行完成后,MountNodeTree会在UI线程被调用,将子线程创建好的UI组件树挂载到UI主树上,使其可以在页面上显示出来。源码参考[NodeCreator.cpp](./entry/src/main/cpp/node/NodeCreator.cpp)
138
139```cpp
140void MountNodeTree(void *asyncUITaskData) {
141    auto asyncData = static_cast<AsyncData*>(asyncUITaskData);
142    if (!asyncData) {
143        return;
144    }
145    auto parent = asyncData->parent;
146    auto child = asyncData->child;
147    // 把组件树挂载到UI主树上。
148    parent->AddChild(child);
149    delete asyncData;
150}
151```
152
153### 性能对比
154
155本示例使用了多线程native接口在非UI线程创建UI组件,减少了UI线程组件创建布局耗时,优化了页面跳转响应时延。
156
157- 使用UI线程创建UI组件
158
159![build_on_ui_thread_trace](figures/build_on_ui_thread_trace.png)
160
161- 使用多线程创建UI组件
162
163![build_on_multi_thread_trace](figures/build_on_multi_thread_trace.png)
164
165|  | UI线程创建 | 多线程创建 | 优化比例 |
166| -------- | -------- | -------- | -------- |
167| UI线程组件创建耗时 | 41.3ms | 5.6ms | 86.4% |
168| UI线程组件创建布局耗时 | 157.7ms | 129.9ms | 17.5% |
169| 响应时延 | 216.4ms | 56.2ms | 74.0% |
170
171### 工程结构&模块类型
172
173    ```
174    |entry/src/main/cpp
175    |   |---card
176    |   |   |---CardCreator.cpp                         // UI卡片创建器实现类
177    |   |   |---CardCreator.h                           // UI卡片创建器声明
178    |   |---common
179    |   |   |---ArkUIBaseNode.h                         // NativeNode封装类,实现组件树操作
180    |   |   |---ArkUINode.h                             // 派生ArkUIBaseNode类,实现属性设置操作
181    |   |   |---NativeModule.h                          // native接口集合获取类
182    |   |---data
183    |   |   |---MockData.h                              // 定义UI卡片内容数据
184    |   |---node
185    |   |   |---NodeCreator.cpp                         // UI组件树创建器实现
186    |   |   |---NodeCreator.h                           // UI组件树创建器声明
187    |   |   |---TypedArkUINode.h                        // 不同类型UI组件封装类
188    |entry/src/main/ets
189    |   |---entryablity
190    |   |   |---EntryAbility.ts                         // 程序入口类
191    |   |---pages
192    |   |   |---Index.ets                               // 首页
193    |   |   |---Page.ets                                // native组件页面
194    ```
195
196### 参考资料
197
198[接入ArkTS页面](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/ui/ndk-access-the-arkts-page.md)
199
200### 相关权限
201
202不涉及。
203
204### 依赖
205
206不涉及。
207
208### 约束与限制
209
2101.本示例仅支持标准系统上运行。
211
2122.本示例为Stage模型,支持API20版本SDK,SDK版本号(API Version 20 Release)。
213
2143.本示例需要使用DevEco Studio版本号(DevEco Studio 5.0.0 Release)及以上版本才可编译运行。
215
216### 下载
217
218如需单独下载本工程,执行如下命令:
219
220```shell
221git init
222git config core.sparsecheckout true
223echo code/UI/NdkBuildOnMultiThread/ > .git/info/sparse-checkout
224git remote add origin https://gitcode.com/openharmony/applications_app_samples.git
225git pull origin master