Native JSBridge Implementation in HarmonyOS Next Applications
Applicable Architecture
This solution is suitable for applications developed using a mix of ArkTS and C++. It is particularly recommended for applications with an architecture similar to a mini-program, which already includes a C++ environment. By utilizing the ArkWeb_ControllerAPI and ArkWeb_ComponentAPI provided on the Native side, developers can implement JSBridge functionality efficiently.
Mini-Program Architecture
Below is a general architecture of a mini-program. The logic layer relies on the JavaScript runtime provided by the application, which operates within an existing C++ environment. Through Native interfaces, the logic layer communicates directly with the view layer (where ArkWeb acts as the renderer) within the C++ environment, eliminating the need to fall back to the ArkTS environment and use the ArkTS JSBridge interface.
Native JSBridge Benefits
The Native JSBridge approach eliminates redundant switching between the ArkTS environment, allowing callbacks to run on non-UI threads and avoiding UI thread blockages.
Implementing Native JSBridge Communication
Binding ArkWeb with Native Interfaces
On the ArkTS side, declare the ArkWeb component and define a custom identifier (webTag) to establish a mapping relationship between the WebviewController and the webTag.
ArkTS Side
// Define a custom webTag and pass it as a parameter when creating the WebviewController to establish a mapping relationship
webTag: string = 'ArkWeb1';
controller: web_webview.WebviewController = new web_webview.WebviewController(this.webTag);
// In aboutToAppear, pass the webTag to the C++ side using the Node-API interface as the unique identifier for the ArkWeb component on the C++ side
aboutToAppear() {
console.info("aboutToAppear");
// Initialize the web ndk
testNapi.nativeWebInit(this.webTag);
}
C++ Side
// Parse and store the webTag
static napi_value NativeWebInit(napi_env env, napi_callback_info info) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit start");
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// Retrieve the first parameter webTag
size_t webTagSize = 0;
napi_get_value_string_utf8(env, args[0], nullptr, 0, &webTagSize);
char *webTagValue = new (std::nothrow) char[webTagSize + 1];
size_t webTagLength = 0;
napi_get_value_string_utf8(env, args[0], webTagValue, webTagSize + 1, &webTagLength);
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit webTag:%{public}s", webTagValue);
// Store the webTag in the instance object
jsbridge_object_ptr = std::make_shared<JSBridgeObject>(webTagValue);
// ...
}
Obtaining API Structures on the Native Side
Before invoking Native APIs on the ArkWeb Native side, you must first obtain the API structure. Use the OH_ArkWeb_GetNativeAPI
function with different type
parameters to retrieve the function pointer structures for ArkWeb_ControllerAPI
and ArkWeb_ComponentAPI
.
static ArkWeb_ControllerAPI *controller = nullptr;
static ArkWeb_ComponentAPI *component = nullptr;
...
controller = reinterpret_cast<ArkWeb_ControllerAPI *>(OH_ArkWeb_GetNativeAPI(ARKWEB_NATIVE_CONTROLLER));
component = reinterpret_cast<ArkWeb_ComponentAPI *>(OH_ArkWeb_GetNativeAPI(ARKWEB_NATIVE_COMPONENT));
Registering Component Lifecycle Callbacks on the Native Side
Use ArkWeb_ComponentAPI
to register component lifecycle callbacks. Before calling these APIs, it is recommended to check if the function pointers exist using ARKWEB_MEMBER_MISSING
to avoid crashes caused by SDK and device ROM mismatches.
if (!ARKWEB_MEMBER_MISSING(component, onControllerAttached)) {
component->onControllerAttached(webTagValue, ValidCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onControllerAttached func not exist");
}
if (!ARKWEB_MEMBER_MISSING(component, onPageBegin)) {
component->onPageBegin(webTagValue, LoadStartCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onPageBegin func not exist");
}
if (!ARKWEB_MEMBER_MISSING(component, onPageEnd)) {
component->onPageEnd(webTagValue, LoadEndCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onPageEnd func not exist");
}
if (!ARKWEB_MEMBER_MISSING(component, onDestroy)) {
component->onDestroy(webTagValue, DestroyCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onDestroy func not exist");
}
Calling Application-Side Functions from Frontend Pages
Register application-side functions to the frontend page using registerJavaScriptProxy
. It is recommended to register these functions in the onControllerAttached
callback to ensure they take effect immediately.
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk RegisterJavaScriptProxy begin");
ArkWeb_ProxyMethod method1 = {"method1", ProxyMethod1, static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
ArkWeb_ProxyMethod method2 = {"method2", ProxyMethod2, static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
ArkWeb_ProxyMethod methodList[2] = {method1, method2};
// Call the NDK interface to register the object
// After registration, in the H5 page, you can use proxy.method1 and proxy.method2 to call the ProxyMethod1 and ProxyMethod2 methods defined here
ArkWeb_ProxyObject proxyObject = {"ndkProxy", methodList, 2};
controller->registerJavaScriptProxy(webTag, &proxyObject);
Calling Frontend Page Functions from the Application Side
Invoke frontend page functions using runJavaScript
.
// Construct the runJS execution structure
char* jsCode = "runJSRetStr()";
ArkWeb_JavaScriptObject object = {(uint8_t *)jsCode, bufferSize, &JSBridgeObject::StaticRunJavaScriptCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
// Call the frontend page function runJSRetStr()
controller->runJavaScript(webTagValue, &object);
Top comments (0)