How to Boost HarmonyOS UI Performance with a C‑Based Rendering Bridge
This article examines four major performance bottlenecks when rendering UI on HarmonyOS—excessive view hierarchy, lengthy communication pipelines, poor list rendering, and secondary layout passes—and introduces a C‑language native interface solution that streamlines component integration, reduces overhead, and aligns HarmonyOS rendering with iOS and Android.
Background
In the dynamic cross‑platform HarmonyOS solution article, we described how UI rendering on HarmonyOS uses system components for recursive rendering. While iOS and Android also rely on their native components, HarmonyOS suffers from four serious issues:
1. Excessive UI hierarchy
For example, a financial app’s "7‑day investment" element has 52 layers on HarmonyOS versus 30 on iOS, causing frame drops and stutter.
2. Lengthy communication process
JS runs in the built‑in V8 VM, ArkTS runs in Huawei’s Ark VM, and communication must pass through JS → C++ → ArkTS, incurring high cross‑language overhead.
3. Poor list rendering performance
Long‑list rendering is critical on iOS, Android, and HarmonyOS. Although Huawei offers several optimizations, third‑party frameworks still struggle with performance on complex list scenarios due to ArkUI’s design.
4. Secondary layout passes
After setting layout properties on HarmonyOS components, the system performs an additional layout pass, adding extra overhead.
New Solution Practice
1. Problem Analysis
UI hierarchy : Recursive rendering with system components requires wrapping each custom component in an extra container (e.g., a
Stack), increasing depth.
<code>@Componentexport
struct RomaDiv {
build(){
Stack(){
// Use wrapBuilder for recursion
ForEach(this.childrenTags, (childrenTag) => {
RomaComponentFactory.builder()
})
}
}
}
</code>Communication length : JS runs in V8, ArkTS in Ark VM, and data must cross multiple threads and language boundaries, inflating latency.
List rendering : HarmonyOS’s reactive system performs dependency collection similar to Vue; when data changes, recursive analysis and off‑screen node updates degrade performance.
Secondary layout : Although Yoga layout calculations happen before setting properties, HarmonyOS does not recognize this, leading to a second layout pass.
2. New Solution Overview
Huawei provides a C‑language imperative interface that sits between native UI components and the ArkTS layer. This C API bypasses state‑management overhead, avoids costly JS‑to‑C++‑to‑ArkTS type conversions, and delivers better performance.
Using the C interface, UI hierarchy aligns with iOS/Android, communication shortens to JS → C++, and list rendering can be controlled directly, allowing pre‑rendering and eliminating secondary layout.
3. How to Use
In a dynamic HarmonyOS cross‑platform project, ArkTS components may nest C components (and vice‑versa). The following demo shows complex nesting scenarios.
3.1 ArkTS Inserting C Component Example
The process consists of three steps:
Create a
NodeContentmanager.
Place a
ContentSlotin the
buildfunction.
Create the native node via C API.
<code>import entry from 'libentry.so';
import { NodeContent } from '@ohos.arkui.node'
@Entry
@Component
struct CMixArkTS {
// 1. NodeContent manager
private divNodeContent: NodeContent = new NodeContent();
build(){
// 2. ContentSlot placeholder
ContentSlot(this.divNodeContent);
}
aboutToAppear(): void {
// 3. Create native node (CAPI)
entry.CreateNativeDivNode(this.divNodeContent);
}
}
</code>The native C implementation of
CreateNativeDivNode:
<code>// 1. C component – green border
static napi_value CreateNativeDivNode(napi_env env, napi_callback_info info) {
if ((env == nullptr) || (info == nullptr)) {
return nullptr;
}
napi_value returnVal = nullptr;
size_t argc = 1;
napi_value args[1] = {nullptr};
if (napi_get_cb_info(env, info, &argc, args, nullptr, nullptr) != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "napi_init", "CreateNativeNode napi_get_cb_info failed");
}
if (argc != 1) {
return nullptr;
}
// Get NodeContent handle from ArkTS side
OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &nodeContentHandle_);
static ArkUI_NativeNodeAPI_1 *nodeAPI = nullptr;
if (nodeAPI == nullptr) {
nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>(
OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1"));
}
if (nodeAPI != nullptr && nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) {
ArkUI_NodeHandle DivComponent = CreateDivNodeHandle();
OH_ArkUI_NodeContent_AddNode(nodeContentHandle_, DivComponent);
}
return returnVal;
}
static ArkUI_NodeHandle CreateDivNodeHandle() {
ArkUI_NodeHandle greenDivNodeHandle = CreateDivNodeHandleWithParam(200, 0xFF00FF00);
CAPIManager::GetInstance()->greenDivNodeHandle = greenDivNodeHandle;
return greenDivNodeHandle;
}
</code>The detailed attribute setting for the div node:
<code>static ArkUI_NodeHandle CreateDivNodeHandleWithParam(float height, uint32_t borderColor) {
ArkUI_NodeHandle divNode = CAPIManager::getNodeAPI()->createNode(ARKUI_NODE_FLEX);
// margin
ArkUI_NumberValue number = {.f32 = 5};
ArkUI_AttributeItem marginValue = {.value = &number, .size = 1};
// border width
ArkUI_NumberValue number2 = {.f32 = 2};
ArkUI_AttributeItem borderWValue = {.value = &number2, .size = 1};
// border color
ArkUI_NumberValue number1 = {.u32 = borderColor};
ArkUI_AttributeItem borderColorItem = {.value = &number1, .size = 1};
// width & height
ArkUI_NumberValue number3 = {.f32 = height};
ArkUI_AttributeItem hValue = {.value = &number3, .size = 1};
ArkUI_NumberValue number5 = {.f32 = 0.9};
ArkUI_AttributeItem wValue = {.value = &number5, .size = 1};
// alignment
ArkUI_NumberValue number4 = {.i32 = ARKUI_ITEM_ALIGNMENT_CENTER};
ArkUI_AttributeItem alignment = {.value = &number4, .size = 1};
CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_MARGIN, &marginValue);
CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_BORDER_WIDTH, &borderWValue);
CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_BORDER_COLOR, &borderColorItem);
CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_WIDTH_PERCENT, &wValue);
CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_HEIGHT, &hValue);
CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_ALIGN_SELF, &alignment);
return divNode;
}
</code>Although the creation process is complex, mastering these core steps allows developers to follow the documentation and implement native nodes.
3.2 Other Scenarios
Beyond the ArkTS‑to‑C example, developers can explore additional integration patterns in the official documentation.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.