Mobile Development 13 min read

Bridge Native Threads to ArkTS in HarmonyOS with Node‑API Thread‑Safe Functions

This article explains how to use Node‑API’s thread‑safe functions to asynchronously relay native file upload progress, completion, and error callbacks from native C/C++ threads to ArkTS JavaScript on HarmonyOS, covering API definitions, event‑loop integration, and practical implementation details with code examples.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Bridge Native Threads to ArkTS in HarmonyOS with Node‑API Thread‑Safe Functions

Introduction

HarmonyOS is an emerging smart operating system. When adapting applications for HarmonyOS, developers often need to maintain three codebases for iOS, Android, and HarmonyOS, especially for features like file upload and download.

The challenge is how to asynchronously callback native file upload/download progress from a native thread to the ArkTS (JavaScript) thread.

Answer: Use the thread‑safe functions provided by Node‑API to forward progress information from the native thread to the ArkTS thread.

What is Node‑API?

Node‑API (formerly N‑API) is an API for building native functionality that is independent of the underlying JavaScript engine (e.g., V8) and is maintained as part of Node.js. HarmonyOS Native API wraps and re‑implements parts of the Node‑API standard, enabling interaction between ArkTS/JS and C/C++ modules.

Key rule: Node‑API calls must be made on the same thread as the JS call; the napi_env cannot be used across threads.

Thread‑Safe Function (ThreadSafeFunction, TFS)

A ThreadSafeFunction allows a normal JS function to be wrapped so it can be called from other threads. Its definition is:

NAPI_EXTERN napi_status napi_create_threadsafe_function(
    napi_env env,
    napi_value func,
    napi_value async_resource,
    napi_value async_resource_name,
    size_t max_queue_size,
    size_t initial_thread_count,
    void* thread_finalize_data,
    napi_finalize thread_finalize_cb,
    void* context,
    napi_threadsafe_function_call_js call_js_cb,
    napi_threadsafe_function* result);

Important parameters:

env : the environment of the API call.

func : the JS callback function.

async_resource : optional object associated with async hooks.

async_resource_name : a string identifier for async hooks.

max_queue_size : maximum size of the event queue (0 = unlimited).

initial_thread_count : number of threads that will use the function.

thread_finalize_cb : optional cleanup callback when the ThreadSafeFunction is destroyed.

call_js_cb : the native callback that will invoke the JS function on the main thread.

Typical Usage Steps

On the native side, create a ThreadSafeFunction, binding the ArkTS callback (e.g., onProgressFun) and the native thread‑safe callback ( call_js_cb).

In the native worker thread, perform the asynchronous task and call napi_call_threadsafe_function to schedule the JS callback via the event loop.

Inside call_js_cb, use napi_call_function to invoke the original JS callback, allowing UI updates on the ArkTS thread.

Event Loop (libuv) Overview

libuv is a C‑based, event‑driven asynchronous I/O library. A minimal example:

#include "stdio.h"
#include "uv.h"

int main() {
  uv_loop_t *loop = uv_default_loop();
  printf("hello libuv");
  uv_run(loop, UV_RUN_DEFAULT);
}

libuv drives the event loop, repeatedly pulling events from the watcher queue, using epoll (on Linux) to wait for I/O events, and dispatching callbacks, which is how ThreadSafeFunction callbacks reach the main thread.

File Upload Scenario in HarmonyOS

The article demonstrates a real‑world case: uploading video or image files in the HarmonyOS Sohu News app. The upload logic is off‑loaded to native code (using curl and openssl), while progress, completion, and error callbacks are sent back to ArkTS via ThreadSafeFunction.

Design Overview

1. Create the ThreadSafeFunction on the ArkTS thread, binding onProgressFun, onCompleteFun, and onErrorFun. 2. In the native upload wrapper, enqueue the actual upload task to a thread pool. 3. The native upload thread reports progress via UploadBridge::callUploadOnProgress, which triggers napi_call_threadsafe_function. 4. The main thread’s callJsUploadOnProgress receives the progress value and invokes the ArkTS onProgressFun, updating the UI.

Key API Definitions

uploadFile

(JS): Exposes the upload interface to ArkTS with parameters uploadUrl, filePath, and three callbacks. NAPI_Global_uploadFile (native): Entry point called from JS, parses arguments, and forwards to MultiFileManager::uploadFileWrapper. MultiFileManager::uploadFileWrapper: Creates the ThreadSafeFunction and dispatches the upload task to a thread pool. UploadBridge::createThreadUploadCallBack: Calls napi_create_threadsafe_function to set up the thread‑safe function. UploadBridge::callJsUploadOnProgress: Native callback that converts the progress integer to a JS value and calls the JS function. MultiFileRequest::uploadReadData: Reads file chunks, updates progress, and invokes UploadBridge::callUploadOnProgress.

Sample Code Snippets

/**
 * File upload JS API
 */
export const uploadFile: (uploadUrl: string, filePath: string,
  onProgressFun: (progress: number) => void,
  onCompleteFun: () => void,
  onErrorFun: (errorCode: number, errorDesc: string) => void) => void;
static napi_value NAPI_Global_uploadFile(napi_env env, napi_callback_info info) {
    size_t argc = 5;
    napi_value args[5];
    // ... argument parsing omitted ...
    MultiFileManager::getInstance()->uploadFileWrapper(uploadUrl, filePath, env, args[2], args[3], args[4]);
    return nullptr;
}
void UploadBridge::createThreadUploadCallBack(napi_env env, napi_value onProgressFun,
                                            napi_value onCompleteFun, napi_value onErrorFun) {
    napi_value workName;
    napi_create_string_utf8(env, "workItem", NAPI_AUTO_LENGTH, &workName);
    napi_create_threadsafe_function(env, onProgressFun, NULL, workName, 0, 1,
                                   NULL, NULL, NULL, callJsUploadOnProgress, &onProgressTsFn);
    // ... other callbacks omitted ...
}

Conclusion

By leveraging Node‑API’s ThreadSafeFunction and the libuv event loop, developers can safely transmit progress, completion, and error information from native upload threads to the ArkTS UI thread in HarmonyOS, enabling responsive UI updates without risking crashes caused by cross‑thread napi_env misuse.

Native、ArkTs线程异步回调
Native、ArkTs线程异步回调
HarmonyOSFile UploadNative integrationArkTSNode-APIThreadSafeFunction
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.