How Dynamic Cross‑Platform UI Rendering Works on HarmonyOS, Android, iOS, and Web
This article explains the architecture and implementation of the Roma dynamic cross‑platform solution, covering its JavaScript‑VM based rendering pipeline, HarmonyOS integration challenges, resource packaging, view creation across JS, C++, and ArkTS layers, and the diff‑based update mechanism that enables seamless UI updates on multiple platforms.
1. Introduction
Through this article you will gain a clear understanding of the implementation principles of dynamic cross‑platform rendering, following the journey of the Roma engine from code packaging to native view drawing.
2. Principle Overview
2.1 Dynamic Cross‑Platform Principle
Dynamic‑Roma is a self‑developed one‑stop cross‑platform solution that runs the same code on Android, iOS, HarmonyOS and Web. Its principle is similar to React Native and Weex: each platform provides a JavaScript virtual machine that loads bundled JS, parses it into instructions, and invokes native host capabilities to transfer data and render views.
Special considerations for HarmonyOS include the ArkTS language and the ArkVM which only loads compiled abc files, requiring a V8 port and a JSVM‑API provided by Huawei.
Can the ArkVM load JS files directly?
Can the JS VM efficiently execute dynamic instructions?
How to integrate the SDK on HarmonyOS?
Roma provides a static library libRomaSdk.so that can be linked via CMake/Ninja; after adding the dependency the dynamic capabilities are available.
2.2 HarmonyOS Integration
At app start the SDK loads a JS Engine and a Jue Instance, initializing the runtime, managing instances, virtual DOM, layout and event handling.
A unified external interface (JS Engine Interface) defines instance creation, lifecycle, element CRUD, bidirectional communication and hot‑reload, allowing each platform to implement the same contract.
In the JS Engine environment a V‑Dom Tree is built, then the native side creates a Render Tree based on it, which is finally laid out and displayed.
3. Business Example
A simple dynamic page in JD Finance demonstrates loading, updating an image and changing a div background color.
3.1 Business Code
The page is written in a Vue‑like language Jue (template, script, style).
<template style="border-width: 2px;">
<div class="normalClass" style="margin-top: 100px;">
<image class="normalClass" :src="imageUrl" style="height: 200;"></image>
<div class="normalClass" :style="{'background-color':bgColor}" style="height:60px;">
<text style="border-width: 2px;width:260px;height:40px;align-self: center;text-align: center;background-color: white;" @click="change()">更新节点数据</text>
</div>
</div>
</template>
<script>
export default {
data() { return { bgColor: "white", imageUrl: "https://static.foodtalks.cn/company/images/434/121logo.png" } },
methods: { change() { this.bgColor = "red"; this.imageUrl = "https://www.szniego.com/uploads/image/20210202/1612232326.png"; this.updateInstance(); } }
}
</script>
<style scoped>
.normalClass { margin: 10px; justify-content: center; align-items: center; align-self: stretch; border-width: 2px; }
</style>3.2 Resource Loading Process
The ADemo.jue file is packaged into ADemo.zip, delivered to the client, unpacked, decrypted and the resulting ADemo.js is loaded by the JS VM to start view rendering.
3.3 Product Code
Key parts of ADemo.js are shown, illustrating how templates are transformed into node creation calls.
/******/ (function(modules) { })
/******/ ({
/***/ "./src/jueDemoList/ADemo/ADemo.jue":
/*!*******************************************!\
!*** ./src/jueDemoList/ADemo/ADemo.jue ***!
\*******************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
var template = JRTemplateManager._jr_create_jue_template('ADemo.jue', {"id":"ADemo","version":"11","dependencies":{"JSEngine":"0.9.4"}});
JRTemplateManager._jr_create_t_node('ADemo.jue','0','40e129af-5e6a-70b8-a757-28a22785dc2f','document',{"style":"border-width: 2px;"});
... (additional generated node creation code) ...
});
//# sourceMappingURL=ADemo.js.map4. View Rendering Process
4.1 Rendering Principle
After the resource is loaded, the native side creates an Instance via N‑API, which calls the JS Engine to build the V‑Dom Tree, passes it to C++ to construct a Component Tree, and finally the Render Tree is built in ArkTS and displayed.
The three language environments (JS, C++, ArkTS) each maintain an independent Instance that correspond to UI, data, and event threads.
4.2 Code Analysis
Key native methods such as aboutToAppear , createInstance , startInstance , and the JSI bridges are examined, showing how instances are created in each environment and how mutations are propagated.
public void aboutToAppear() {
romaAssetsManager.ensureAsset(this.jueName, progress).then(version => {
this.romaInstance = this.createInstance(this.rootContent, this.pageId);
this.romaInstance!.startInstance(this.initialProps).then(result => {});
}).catch(error => {});
}
private RomaInstance createInstance(NodeContent root, String pageId) {
return RomaEnv.createAndRegisterRomaInstance(this.jueName, new RomaInstanceParam(root, getUIContext(), getContext(this), pageId, errorListener), this.stateListener);
}
public async startInstance(TObject initialProps) {
return this.napiBridge.startInstance(this.getId(), initialProps);
}4.3 Rendering in ArkTS
The RomaComponentFactory builds concrete components (document, div, text, image) based on the descriptor generated from the Render Tree.
@Builder
function RomaComponentFactoryBuilder(param) {
if (param.type == "document") { RomaDocument({componentCtx: param.componentCtx}); }
else if (param.type == "div") { RomaDiv({componentCtx: param.componentCtx}); }
else if (param.type === "text") { RomaText({componentCtx: param.componentCtx}); }
else if (param.type === "image" || param.type === "img") { RomaImageView({componentCtx: param.componentCtx}); }
else { RomaCustomComponentFactory.customComponentBuilder.builder(param.componentCtx); }
}5. View Update Process
When the “Update node data” button is pressed, the image URL and div background are changed. The update follows the same three‑instance pipeline, using a diff algorithm to compare the new V‑Dom Tree with the old one and apply only the changed nodes.
5.1 Diff Principle
A new V‑Dom Tree is generated, diffed against the previous tree, and the resulting mutation list updates the Render Tree.
6. Planning Summary
Three threads ensure efficient processing across environments, but the current ArkUI component hierarchy adds overhead. Future work will integrate Huawei’s C‑API to render directly in C++, reducing component depth and cross‑language communication cost.
Dynamic Roma spans Android, iOS, HarmonyOS, Web, Java, C/C++, Vue, JavaScript, Node, Webpack, Clang, Ninja, and invites further discussion.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
