DEV Community

HarmonyOS
HarmonyOS

Posted on

Node API Part-7 : Creating an ArkTS Runtime in Native Threads Using Node-API on HarmonyOSNext

Read the original article:Node API Part-7 : Creating an ArkTS Runtime in Native Threads Using Node-API on HarmonyOSNext

🎯Node API Part-7 : Creating an ArkTS Runtime in Native Threads Using Node-API on HarmonyOSNex

Introduction

💡 As HarmonyOS continues to evolve, the need to bridge native C++ performance with the flexibility of ArkTS (Ark TypeScript) becomes increasingly essential — especially in high-performance or multithreaded scenarios. While ArkTS powers the UI and logic layers of many HarmonyOS applications, native modules are often indispensable for hardware-level operations or concurrent processing.

However, a challenge arises: ArkTS runtime environments are not created automatically in native threads. Without an active runtime, developers cannot directly execute ArkTS functions from native code. Thankfully, HarmonyOS offers a solution via the Node-API (NAPI). In this article, we’ll walk through how to manually create an ArkTS runtime within a native C++ thread, dynamically load an ArkTS module, and invoke functions — bringing together the best of both worlds.

Creating the Runtime: The Core Workflow

To enable communication between native and ArkTS code, we follow a structured approach:

  • Register a Native Module: Declare a native method using Node-API and bind it to the createArkRuntime function.
  • Use C++ Threads: Create a new pthread that hosts the runtime.
  • Initialize ArkTS Runtime: Use napi_create_ark_runtime() inside the thread to create a runtime environment.
  • Dynamically Load ETS Modules: Load an ETS module using napi_load_module_with_info().
  • Invoke Functions: Call ArkTS-exported functions such as a Logger() using napi_call_function().
  • Clean Up: Destroy the runtime after execution using napi_destroy_ark_runtime().

This pattern enables native modules to safely and efficiently execute ArkTS logic even in multi-threaded contexts.

⚙️ Step-by-Step Implementation/h3>

1. 📁 API Declaration in index.d.ts


export const createArkRuntime: () => object;

Enter fullscreen mode Exit fullscreen mode

API Interface Declaration

2. 📁 Main Logics in create_ark_runtime.cpp

#include <pthread.h>
#include "napi/native_api.h"

// Thread function to create and manage the ArkTS runtime environment
static void *CreateArkRuntimeFunc(void *arg) {
    // 1. Create the ArkTS runtime environment
    napi_env env;
    napi_status ret = napi_create_ark_runtime(&env);
    if (ret != napi_ok) {
        return nullptr; // Failed to create runtime
    }

    // 2. Load a custom module from the specified path and bundle name
    napi_value objUtils;
    ret = napi_load_module_with_info(env, "entry/src/main/ets/pages/ObjectUtils", "com.huawei.myapplication/entry",
                                     &objUtils);
    if (ret != napi_ok) {
        return nullptr; // Failed to load module
    }

    // 3. Access a named export "Logger" from the loaded module
    napi_value logger;
    ret = napi_get_named_property(env, objUtils, "Logger", &logger);
    if (ret != napi_ok) {
        return nullptr; // Failed to get "Logger" property
    }

    // 4. Call the "Logger" function (assumed to log something)
    ret = napi_call_function(env, objUtils, logger, 0, nullptr, nullptr);

    // 5. Destroy the ArkTS runtime environment after use
    ret = napi_destroy_ark_runtime(&env);
    return nullptr;
}

// Native callback exposed to JS that creates a thread to run ArkTS logic
static napi_value CreateArkRuntime(napi_env env, napi_callback_info info) {
    pthread_t tid;
    pthread_create(&tid, nullptr, CreateArkRuntimeFunc, nullptr); // Start thread
    pthread_join(tid, nullptr); // Wait for thread to complete
    return nullptr;
}

EXTERN_C_START
// Initialization function called when the module is loaded
static napi_value Init(napi_env env, napi_value exports) {
    // Define the "createArkRuntime" function to be exported to JS
    napi_property_descriptor desc[] = {
        {"createArkRuntime", nullptr, CreateArkRuntime, nullptr, nullptr, nullptr, napi_default, nullptr}};
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

// Define the native module and register it with Node-API
static napi_module nativeModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = nullptr,
    .reserved = {0},
};

// Register the native module when the shared object is loaded
extern "C" __attribute__((constructor)) void RegisterQueueWorkModule() {
    napi_module_register(&nativeModule);
}

Enter fullscreen mode Exit fullscreen mode

create_ark_runtime.cpp

3. Call for creating the environment in ArkTS

import testNapi from 'libentry.so';

@Entry
@Component
struct Index {
  @State message: string = 'Create Ark Runtime';

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            testNapi.createArkRuntime();
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

Enter fullscreen mode Exit fullscreen mode

Index.ets

4. Run in parallel ArkTS Script on the new thread

export function Logger() {
  console.log("Console Log");
}
Enter fullscreen mode Exit fullscreen mode

ObjectUtil.ets

5. Set the Config in CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(NDKBasics)

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

include_directories(${NATIVERENDER_ROOT_PATH}
                    ${NATIVERENDER_ROOT_PATH}/include)

add_library(entry SHARED create_ark_runtime.cpp)
target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so)
Enter fullscreen mode Exit fullscreen mode

CMakeLists.txt

✅ Key Benefits

  1. Fine-Grained Runtime Control
    Manually managing ArkTS runtimes allows developers to control when and how the JavaScript engine is initialized and destroyed — leading to precise resource management.

  2. Multithreaded Execution Support
    By creating the ArkTS runtime in a separate pthread, native code can execute ArkTS logic asynchronously without blocking the main thread.

  3. Seamless Native-ArkTS Integration
    Dynamically invoking ArkTS modules from native C++ bridges the performance of native code with the flexibility of TypeScript logic.

  4. Modular and Reusable Architecture
    ArkTS modules like Logger can be reused and invoked from multiple native threads or contexts, encouraging code modularity and separation of concerns.

  5. Improved Performance for Background Tasks
    Background operations (e.g., logging, monitoring, telemetry) can be offloaded to ArkTS modules running inside isolated runtimes, improving main thread performance.

  6. Dynamic Loading of Logic at Runtime
    ETS modules can be loaded and executed dynamically, enabling flexible logic dispatch and potentially enabling plugin-like architectures.

  7. HarmonyOS Compliance and Safety
    Using official Node-API functions like napi_create_ark_runtime and napi_destroy_ark_runtime ensures compatibility with HarmonyOS runtime constraints and resource limits (e.g., 16 runtimes per process).

Conclusion

✅ Bridging ArkTS and native C++ code in HarmonyOS isn’t just possible — it’s powerful. By creating a dedicated ArkTS runtime inside a native thread using Node-API, developers gain fine-grained control over when and how ArkTS logic is executed. This approach opens doors for performance optimization, background processing, and dynamic module management.

Whether you’re building system-level services or crafting high-performance apps, integrating ArkTS and C++ using this runtime strategy empowers you to scale efficiently within the HarmonyOS ecosystem.

Keep in mind that a maximum of 16 runtime environments per process is supported — design accordingly to ensure stability.

If you’re ready to take full advantage of HarmonyOS’s hybrid architecture, this is a technique worth adding to your toolkit.

📚 Resources

The OpenCms demo, brought to you by Alkacon Software.developer.huawei.com

The OpenCms demo, brought to you by Alkacon Software.developer.huawei.com

The OpenCms demo, brought to you by Alkacon Software.developer.huawei.com

Written by Bunyamin Eymen Alagoz

Top comments (0)