1# 应用侧与前端页面的相互调用(C/C++)
2
3本指导适用于ArkWeb应用侧与前端网页通信场景,开发者可根据应用架构选择使用ArkWeb Native接口完成业务通信机制(以下简称Native JSBridge)。
4
5## 适用的应用架构
6
7应用使用ArkTS、C++语言混合开发,或本身应用架构较贴近于小程序架构,自带C++侧环境,推荐使用ArkWeb在Native侧提供的[ArkWeb_ControllerAPI](../reference/apis-arkweb/_ark_web___controller_a_p_i.md#arkweb_controllerapi)、[ArkWeb_ComponentAPI](../reference/apis-arkweb/_ark_web___component_a_p_i.md#arkweb_componentapi)实现JSBridge功能。
8
9  ![arkweb_jsbridge_arch](figures/arkweb_jsbridge_arch.png)
10
11  上图展示了具有普遍适用性的小程序的通用架构。在这一架构中,逻辑层依赖于应用程序自带的JavaScript运行时,该运行时在一个已有的C++环境中运行。通过Native接口,逻辑层能够直接在C++环境中与视图层(其中ArkWeb充当渲染器)进行通信,无需回退至ArkTS环境使用ArkTS JSBridge接口。
12
13  左图是使用ArkTS JSBridge接口构建小程序的方案,如红框所示,应用需要先调用到ArkTS环境,再调用到C++环境。右图是使用Native JSBridge接口构建小程序的方案,不需要ArkTS环境和C++环境的切换,执行效率更高。
14
15  ![arkweb_jsbridge_diff](figures/arkweb_jsbridge_diff.png)
16
17  Native JSBridge方案可以解决ArkTS环境的冗余切换,同时允许回调在非UI线程上运行,避免造成UI阻塞。
18
19## 使用Native接口实现JSBridge通信
20
21### 使用Native接口绑定ArkWeb
22
23* ArkWeb组件声明在ArkTS侧,需要用户自定义一个标识webTag,并将webTag通过Node-API传至应用Native侧,后续ArkWeb Native接口使用,均需webTag作为对应组件的唯一标识。
24
25* ArkTS侧
26
27  ```js
28  // 自定义webTag,在WebviewController创建时作为入参传入,建立controller与webTag的映射关系
29  webTag: string = 'ArkWeb1';
30  controller: web_webview.WebviewController = new web_webview.WebviewController(this.webTag);
31  ...
32  // aboutToAppear中将webTag通过Node-API接口传入C++侧,作为C++侧ArkWeb组件的唯一标识
33  aboutToAppear() {
34    console.info("aboutToAppear")
35    //初始化web ndk
36    testNapi.nativeWebInit(this.webTag);
37  }
38  ...
39  ```
40
41* C++侧
42
43  ```c++
44  // 解析存储webTag
45  static napi_value NativeWebInit(napi_env env, napi_callback_info info) {
46      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit start");
47      size_t argc = 1;
48      napi_value args[1] = {nullptr};
49      napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
50      // 获取第一个参数webTag
51      size_t webTagSize = 0;
52      napi_get_value_string_utf8(env, args[0], nullptr, 0, &webTagSize);
53      char *webTagValue = new (std::nothrow) char[webTagSize + 1];
54      size_t webTagLength = 0;
55      napi_get_value_string_utf8(env, args[0], webTagValue, webTagSize + 1, &webTagLength);
56      OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit webTag:%{public}s", webTagValue);
57
58      // 将webTag保存在实例对象中
59      jsbridge_object_ptr = std::make_shared<JSBridgeObject>(webTagValue);
60      // ...
61  ```
62
63### 使用Native接口获取API结构体
64
65ArkWeb Native侧得先获取API结构体,才能调用结构体里的Native API。ArkWeb Native侧API通过函数[OH_ArkWeb_GetNativeAPI](../reference/apis-arkweb/_web.md#oh_arkweb_getnativeapi())获取,根据入参type不同,可分别获取[ArkWeb_ControllerAPI](../reference/apis-arkweb/_ark_web___controller_a_p_i.md#arkweb_controllerapi)、[ArkWeb_ComponentAPI](../reference/apis-arkweb/_ark_web___component_a_p_i.md#arkweb_componentapi)函数指针结构体。其中[ArkWeb_ControllerAPI](../reference/apis-arkweb/_ark_web___controller_a_p_i.md#arkweb_controllerapi)对应ArkTS侧[web_webview.WebviewController API](../reference/apis-arkweb/js-apis-webview.md),[ArkWeb_ComponentAPI](../reference/apis-arkweb/_ark_web___component_a_p_i.md#arkweb_componentapi)对应ArkTS侧[ArkWeb组件API](../reference/apis-arkweb/ts-basic-components-web.md)。
66
67  ```c++
68  static ArkWeb_ControllerAPI *controller = nullptr;
69  static ArkWeb_ComponentAPI *component = nullptr;
70  ...
71  controller = reinterpret_cast<ArkWeb_ControllerAPI *>(OH_ArkWeb_GetNativeAPI(ARKWEB_NATIVE_CONTROLLER));
72  component = reinterpret_cast<ArkWeb_ComponentAPI *>(OH_ArkWeb_GetNativeAPI(ARKWEB_NATIVE_COMPONENT));
73  ```
74
75### Native侧注册组件生命周期回调
76
77通过[ArkWeb_ComponentAPI](../reference/apis-arkweb/_ark_web___component_a_p_i.md#arkweb_componentapi)注册组件生命周期回调,在调用API前建议通过[ARKWEB_MEMBER_MISSING](../reference/apis-arkweb/_web.md#arkweb_member_missing)校验该函数结构体是否有对应函数指针,避免SDK与设备ROM不匹配导致crash问题。
78
79  ```c++
80  if (!ARKWEB_MEMBER_MISSING(component, onControllerAttached)) {
81      component->onControllerAttached(webTagValue, ValidCallback,
82                                      static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
83  } else {
84      OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onControllerAttached func not exist");
85  }
86
87  if (!ARKWEB_MEMBER_MISSING(component, onPageBegin)) {
88      component->onPageBegin(webTagValue, LoadStartCallback,
89                                      static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
90  } else {
91      OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onPageBegin func not exist");
92  }
93
94  if (!ARKWEB_MEMBER_MISSING(component, onPageEnd)) {
95      component->onPageEnd(webTagValue, LoadEndCallback,
96                                      static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
97  } else {
98      OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onPageEnd func not exist");
99  }
100
101  if (!ARKWEB_MEMBER_MISSING(component, onDestroy)) {
102      component->onDestroy(webTagValue, DestroyCallback,
103                                      static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
104  } else {
105      OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onDestroy func not exist");
106  }
107  ```
108
109### 前端页面调用应用侧函数
110
111通过[registerJavaScriptProxy](../reference/apis-arkweb/_ark_web___controller_a_p_i.md#registerjavascriptproxy)将应用侧函数注册至前端页面,推荐在[onControllerAttached](../reference/apis-arkweb/_ark_web___component_a_p_i.md#oncontrollerattached)回调中注册,其它时机注册需要手动调用[refresh](../reference/apis-arkweb/_ark_web___controller_a_p_i.md#refresh)才能生效。
112
113  ```c++
114  // 注册对象
115  OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk RegisterJavaScriptProxy begin");
116  ArkWeb_ProxyMethod method1 = {"method1", ProxyMethod1, static_cast<void *>(jsbridge_object_ptr->GetWeakPt  ())};
117  ArkWeb_ProxyMethod method2 = {"method2", ProxyMethod2, static_cast<void *>(jsbridge_object_ptr->GetWeakPt  ())};
118  ArkWeb_ProxyMethod methodList[2] = {method1, method2};
119  // 调用ndk接口注册对象
120  // 如此注册的情况下,在H5页面就可以使用proxy.method1proxy.method2调用此文件下的ProxyMethod1和ProxyMethod2方法了
121  ArkWeb_ProxyObject proxyObject = {"ndkProxy", methodList, 2};
122  controller->registerJavaScriptProxy(webTag, &proxyObject);
123  ```
124
125### 应用侧调用前端页面函数
126
127通过[runJavaScript](../reference/apis-arkweb/_ark_web___controller_a_p_i.md#runjavascript)调用前端页面函数。
128
129  ```c++
130  // 构造runJS执行的结构体
131  char* jsCode = "runJSRetStr()";
132  ArkWeb_JavaScriptObject object = {(uint8_t *)jsCode, bufferSize, &JSBridgeObject::StaticRunJavaScriptCallback,
133                                       static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
134  // 调用前端页面runJSRetStr()函数
135  controller->runJavaScript(webTagValue, &object);
136  ```
137
138### 完整示例
139
140* 前端页面代码
141
142  ```html
143  <!-- entry/src/main/resources/rawfile/runJS.html -->
144  <!-- runJS.html -->
145  <!DOCTYPE html>
146  <html lang="en-gb">
147  <head>
148      <meta name="viewport" content="width=device-width, initial-scale=1.0">
149      <title>run javascript demo</title>
150  </head>
151  <body>
152  <h1>run JavaScript Ext demo</h1>
153  <p id="webDemo"></p>
154  <br>
155  <button type="button" style="height:30px;width:200px" onclick="testNdkProxyObjMethod1()">test ndk method1 ! </button>
156  <br>
157  <br>
158  <button type="button" style="height:30px;width:200px" onclick="testNdkProxyObjMethod2()">test ndk method2 ! </button>
159  <br>
160
161  </body>
162  <script type="text/javascript">
163
164  function testNdkProxyObjMethod1() {
165        if (window.ndkProxy == undefined) {
166              document.getElementById("webDemo").innerHTML = "ndkProxy undefined"
167              return "objName undefined"
168        }
169
170        if (window.ndkProxy.method1 == undefined) {
171              document.getElementById("webDemo").innerHTML = "ndkProxy method1 undefined"
172              return "objName  test undefined"
173        }
174
175        if (window.ndkProxy.method2 == undefined) {
176              document.getElementById("webDemo").innerHTML = "ndkProxy method2 undefined"
177              return "objName  test undefined"
178        }
179        window.ndkProxy.method1("hello", "world", [1.2, -3.4, 123.456], ["Saab", "Volvo", "BMW", undefined], 1.23456, 123789, true, false, 0,  undefined);
180  }
181
182  function testNdkProxyObjMethod2() {
183        if (window.ndkProxy == undefined) {
184              document.getElementById("webDemo").innerHTML = "ndkProxy undefined"
185              return "objName undefined"
186        }
187
188        if (window.ndkProxy.method1 == undefined) {
189              document.getElementById("webDemo").innerHTML = "ndkProxy method1 undefined"
190              return "objName  test undefined"
191        }
192
193        if (window.ndkProxy.method2 == undefined) {
194              document.getElementById("webDemo").innerHTML = "ndkProxy method2 undefined"
195              return "objName  test undefined"
196        }
197
198      var student = {
199              name:"zhang",
200              sex:"man",
201              age:25
202      };
203      var cars = [student, 456, false, 4.567];
204      let params = "[\"{\\\"scope\\\"]";
205
206      window.ndkProxy.method2("hello", "world", false, cars, params);
207  }
208
209  function runJSRetStr(data) {
210      const d = new Date();
211      let time = d.getTime();
212      return JSON.stringify(time)
213  }
214  </script>
215  </html>
216  ```
217
218* ArkTS侧代码
219
220  ```javascript
221  // entry/src/main/ets/pages/Index.ets
222  import testNapi from 'libentry.so';
223  import { webview } from '@kit.ArkWeb';
224
225  class testObj {
226    constructor() {
227    }
228
229    test(): string {
230      console.log('ArkUI Web Component');
231      return "ArkUI Web Component";
232    }
233
234    toString(): void {
235      console.log('Web Component toString');
236    }
237  }
238
239  @Entry
240  @Component
241  struct Index {
242    webTag: string = 'ArkWeb1';
243    controller: webview.WebviewController = new webview.WebviewController(this.webTag);
244    @State testObjtest: testObj = new testObj();
245
246    aboutToAppear() {
247      console.info("aboutToAppear")
248      //初始化web ndk
249      testNapi.nativeWebInit(this.webTag);
250    }
251
252    build() {
253      Column() {
254        Row() {
255          Button('runJS hello')
256            .fontSize(12)
257            .onClick(() => {
258              testNapi.runJavaScript(this.webTag, "runJSRetStr(\"" + "hello" + "\")");
259            })
260        }.height('20%')
261
262        Row() {
263          Web({ src: $rawfile('runJS.html'), controller: this.controller })
264            .javaScriptAccess(true)
265            .fileAccess(true)
266            .onControllerAttached(() => {
267              console.error("ndk onControllerAttached webId: " + this.controller.getWebId());
268            })
269        }.height('80%')
270      }
271    }
272  }
273  ```
274
275* Node-API侧暴露ArkTS接口
276
277  ```javascript
278  // entry/src/main/cpp/types/libentry/index.d.ts
279  export const nativeWebInit: (webName: string) => void;
280  export const runJavaScript: (webName: string, jsCode: string) => void;
281  ```
282
283* Node-API侧编译配置`entry/src/main/cpp/CMakeLists.txt`
284
285  ```c++
286  # the minimum version of CMake.
287  cmake_minimum_required(VERSION 3.4.1)
288  project(NDKJSBridg)
289
290  set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
291
292  if(DEFINED PACKAGE_FIND_FILE)
293      include(${PACKAGE_FIND_FILE})
294  endif()
295
296  include_directories(${NATIVERENDER_ROOT_PATH}
297                      ${NATIVERENDER_ROOT_PATH}/include)
298
299  add_library(entry SHARED hello.cpp jsbridge_object.cpp)
300
301  find_library(
302      # Sets the name of the path variable.
303      hilog-lib
304      # Specifies the name of the NDK library that
305      # you want CMake to locate.
306      hilog_ndk.z
307  )
308
309  target_link_libraries(entry PUBLIC libace_napi.z.so ${hilog-lib} libohweb.so)
310  ```
311
312* Node-API层代码
313
314  ```c++
315  // entry/src/main/cpp/hello.cpp
316  #include "napi/native_api.h"
317  #include <bits/alltypes.h>
318  #include <memory>
319  #include <string>
320  #include <sys/types.h>
321  #include <thread>
322
323  #include "hilog/log.h"
324  #include "web/arkweb_interface.h"
325  #include "jsbridge_object.h"
326
327  constexpr unsigned int LOG_PRINT_DOMAIN = 0xFF00;
328  std::shared_ptr<JSBridgeObject> jsbridge_object_ptr = nullptr;
329  static ArkWeb_ControllerAPI *controller = nullptr;
330  static ArkWeb_ComponentAPI *component = nullptr;
331
332  // 发送JS脚本到H5侧执行,该方法为执行结果的回调。
333  static void RunJavaScriptCallback(const char *webTag, const char *result, void *userData) {
334      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk RunJavaScriptCallback webTag:%{public}s", webTag);
335      if (!userData) {
336          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk RunJavaScriptCallback userData is nullptr");
337          return;
338      }
339      std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
340      if (auto jsb_ptr = jsb_weak_ptr.lock()) {
341          jsb_ptr->RunJavaScriptCallback(result);
342      } else {
343          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
344                       "ndk RunJavaScriptCallback jsb_weak_ptr lock failed");
345      }
346  }
347
348  // 示例代码 ,注册了1个对象,2个方法
349  static void ProxyMethod1(const char *webTag, const ArkWeb_JavaScriptBridgeData *dataArray, size_t arraySize, void *userData) {
350      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod1 webTag:%{public}s", webTag);
351      if (!userData) {
352          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod1 userData is nullptr");
353          return;
354      }
355      std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
356      if (auto jsb_ptr = jsb_weak_ptr.lock()) {
357          jsb_ptr->ProxyMethod1(dataArray, arraySize);
358      } else {
359          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod1 jsb_weak_ptr lock failed");
360      }
361  }
362
363  static void ProxyMethod2(const char *webTag, const ArkWeb_JavaScriptBridgeData *dataArray, size_t arraySize, void *userData) {
364      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod2 webTag:%{public}s", webTag);
365      if (!userData) {
366          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod2 userData is nullptr");
367          return;
368      }
369      std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
370
371      std::string jsCode = "runJSRetStr()";
372      ArkWeb_JavaScriptObject object = {(uint8_t *)jsCode.c_str(), jsCode.size(),
373                                       &JSBridgeObject::StaticRunJavaScriptCallback,
374                                       static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
375      controller->runJavaScript(webTag, &object);
376
377      if (auto jsb_ptr = jsb_weak_ptr.lock()) {
378          jsb_ptr->ProxyMethod2(dataArray, arraySize);
379      } else {
380          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod2 jsb_weak_ptr lock failed");
381      }
382  }
383
384  void ValidCallback(const char *webTag, void *userData) {
385      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ValidCallback webTag: %{public}s", webTag);
386      if (!userData) {
387          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ValidCallback userData is nullptr");
388          return;
389      }
390      std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
391      if (auto jsb_ptr = jsb_weak_ptr.lock()) {
392          jsb_ptr->SaySomething("ValidCallback");
393      } else {
394          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ValidCallback jsb_weak_ptr lock failed");
395      }
396
397      // 注册对象
398      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk RegisterJavaScriptProxy begin");
399      ArkWeb_ProxyMethod method1 = {"method1", ProxyMethod1, static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
400      ArkWeb_ProxyMethod method2 = {"method2", ProxyMethod2, static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
401      ArkWeb_ProxyMethod methodList[2] = {method1, method2};
402      // 调用ndk接口注册对象
403      // 如此注册的情况下,在H5页面就可以使用proxy.method1proxy.method2调用此文件下的ProxyMethod1和ProxyMethod2方法了
404      ArkWeb_ProxyObject proxyObject = {"ndkProxy", methodList, 2};
405      controller->registerJavaScriptProxy(webTag, &proxyObject);
406
407      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk RegisterJavaScriptProxy end");
408  }
409
410  void LoadStartCallback(const char *webTag, void *userData) {
411      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadStartCallback webTag: %{public}s", webTag);
412      if (!userData) {
413          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadStartCallback userData is nullptr");
414          return;
415      }
416      std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
417      if (auto jsb_ptr = jsb_weak_ptr.lock()) {
418          jsb_ptr->SaySomething("LoadStartCallback");
419      } else {
420          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadStartCallback jsb_weak_ptr lock failed");
421      }
422  }
423
424  void LoadEndCallback(const char *webTag, void *userData) {
425      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadEndCallback webTag: %{public}s", webTag);
426      if (!userData) {
427          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadEndCallback userData is nullptr");
428          return;
429      }
430      std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
431      if (auto jsb_ptr = jsb_weak_ptr.lock()) {
432          jsb_ptr->SaySomething("LoadEndCallback");
433      } else {
434          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadEndCallback jsb_weak_ptr lock failed");
435      }
436  }
437
438  void DestroyCallback(const char *webTag, void *userData) {
439      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk DestoryCallback webTag: %{public}s", webTag);
440      if (!userData) {
441          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk DestroyCallback userData is nullptr");
442          return;
443      }
444      std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
445      if (auto jsb_ptr = jsb_weak_ptr.lock()) {
446          jsb_ptr->SaySomething("DestroyCallback");
447      } else {
448          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk DestroyCallback jsb_weak_ptr lock failed");
449      }
450  }
451
452  void SetComponentCallback(ArkWeb_ComponentAPI * component, const char* webTagValue) {
453      if (!ARKWEB_MEMBER_MISSING(component, onControllerAttached)) {
454          component->onControllerAttached(webTagValue, ValidCallback,
455                                          static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
456      } else {
457          OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onControllerAttached func not exist");
458      }
459
460      if (!ARKWEB_MEMBER_MISSING(component, onPageBegin)) {
461          component->onPageBegin(webTagValue, LoadStartCallback,
462                                          static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
463      } else {
464          OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onPageBegin func not exist");
465      }
466
467      if (!ARKWEB_MEMBER_MISSING(component, onPageEnd)) {
468          component->onPageEnd(webTagValue, LoadEndCallback,
469                                          static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
470      } else {
471          OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onPageEnd func not exist");
472      }
473
474      if (!ARKWEB_MEMBER_MISSING(component, onDestroy)) {
475          component->onDestroy(webTagValue, DestroyCallback,
476                                          static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
477      } else {
478          OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onDestroy func not exist");
479      }
480  }
481
482  // 解析存储webTag
483  static napi_value NativeWebInit(napi_env env, napi_callback_info info) {
484      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit start");
485      size_t argc = 1;
486      napi_value args[1] = {nullptr};
487      napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
488      // 获取第一个参数webTag
489      size_t webTagSize = 0;
490      napi_get_value_string_utf8(env, args[0], nullptr, 0, &webTagSize);
491      char *webTagValue = new (std::nothrow) char[webTagSize + 1];
492      size_t webTagLength = 0;
493      napi_get_value_string_utf8(env, args[0], webTagValue, webTagSize + 1, &webTagLength);
494      OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit webTag:%{public}s", webTagValue);
495
496      // 将webTag保存在实例对象中
497      jsbridge_object_ptr = std::make_shared<JSBridgeObject>(webTagValue);
498      if (jsbridge_object_ptr)
499          jsbridge_object_ptr->Init();
500
501      controller = reinterpret_cast<ArkWeb_ControllerAPI *>(OH_ArkWeb_GetNativeAPI(ARKWEB_NATIVE_CONTROLLER));
502      component = reinterpret_cast<ArkWeb_ComponentAPI *>(OH_ArkWeb_GetNativeAPI(ARKWEB_NATIVE_COMPONENT));
503      SetComponentCallback(component, webTagValue);
504
505      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit end");
506      return nullptr;
507  }
508
509  // 发送JS脚本到H5侧执行
510  static napi_value RunJavaScript(napi_env env, napi_callback_info info) {
511      size_t argc = 2;
512      napi_value args[2] = {nullptr};
513      napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
514
515      // 获取第一个参数webTag
516      size_t webTagSize = 0;
517      napi_get_value_string_utf8(env, args[0], nullptr, 0, &webTagSize);
518      char *webTagValue = new (std::nothrow) char[webTagSize + 1];
519      size_t webTagLength = 0;
520      napi_get_value_string_utf8(env, args[0], webTagValue, webTagSize + 1, &webTagLength);
521      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk OH_NativeArkWeb_RunJavaScript webTag:%{public}s",
522                   webTagValue);
523
524      // 获取第二个参数 jsCode
525      size_t bufferSize = 0;
526      napi_get_value_string_utf8(env, args[1], nullptr, 0, &bufferSize);
527      char *jsCode = new (std::nothrow) char[bufferSize + 1];
528      size_t byteLength = 0;
529      napi_get_value_string_utf8(env, args[1], jsCode, bufferSize + 1, &byteLength);
530
531      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
532                   "ndk OH_NativeArkWeb_RunJavaScript jsCode len:%{public}zu", strlen(jsCode));
533
534      // 构造runJS执行的结构体
535      ArkWeb_JavaScriptObject object = {(uint8_t *)jsCode, bufferSize, &JSBridgeObject::StaticRunJavaScriptCallback,
536                                       static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
537      controller->runJavaScript(webTagValue, &object);
538      return nullptr;
539  }
540
541  EXTERN_C_START
542  static napi_value Init(napi_env env, napi_value exports) {
543      napi_property_descriptor desc[] = {
544          {"nativeWebInit", nullptr, NativeWebInit, nullptr, nullptr, nullptr, napi_default, nullptr},
545          {"runJavaScript", nullptr, RunJavaScript, nullptr, nullptr, nullptr, napi_default, nullptr},
546      };
547      napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
548      return exports;
549  }
550  EXTERN_C_END
551
552  static napi_module demoModule = {
553      .nm_version = 1,
554      .nm_flags = 0,
555      .nm_filename = nullptr,
556      .nm_register_func = Init,
557      .nm_modname = "entry",
558      .nm_priv = ((void *)0),
559      .reserved = {0},
560  };
561
562  extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
563  ```
564
565* Native侧业务代码
566
567  ```c++
568  // entry/src/main/cpp/jsbridge_object.h
569  #include "web/arkweb_type.h"
570  #include <string>
571
572  class JSBridgeObject : public std::enable_shared_from_this<JSBridgeObject> {
573  public:
574      JSBridgeObject(const char* webTag);
575      ~JSBridgeObject() = default;
576      void Init();
577      std::weak_ptr<JSBridgeObject>* GetWeakPtr();
578      static void StaticRunJavaScriptCallback(const char *webTag, const ArkWeb_JavaScriptBridgeData *data, void *userData);
579      void RunJavaScriptCallback(const char *result);
580      void ProxyMethod1(const ArkWeb_JavaScriptBridgeData *dataArray, int32_t arraySize);
581      void ProxyMethod2(const ArkWeb_JavaScriptBridgeData *dataArray, int32_t arraySize);
582      void SaySomething(const char* say);
583
584  private:
585      std::string webTag_;
586      std::weak_ptr<JSBridgeObject> weak_ptr_;
587  };
588  ```
589
590  ```c++
591  // entry/src/main/cpp/jsbridge_object.cpp
592  #include "jsbridge_object.h"
593
594  #include "hilog/log.h"
595
596  constexpr unsigned int LOG_PRINT_DOMAIN = 0xFF00;
597
598  JSBridgeObject::JSBridgeObject(const char *webTag) : webTag_(webTag) {}
599
600  void JSBridgeObject::Init() { weak_ptr_ = shared_from_this(); }
601
602  std::weak_ptr<JSBridgeObject> *JSBridgeObject::GetWeakPtr() { return &weak_ptr_; }
603
604  void JSBridgeObject::StaticRunJavaScriptCallback(const char *webTag, const ArkWeb_JavaScriptBridgeData *data,
605                                                   void *userData) {
606      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
607                   "JSBridgeObject StaticRunJavaScriptCallback webTag:%{public}s", webTag);
608      if (!userData) {
609          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
610                       "JSBridgeObject StaticRunJavaScriptCallback userData is nullptr");
611          return;
612      }
613      std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
614      if (auto jsb_ptr = jsb_weak_ptr.lock()) {
615          std::string result((char *)data->buffer, data->size);
616          jsb_ptr->RunJavaScriptCallback(result.c_str());
617      } else {
618          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
619                       "JSBridgeObject StaticRunJavaScriptCallback jsb_weak_ptr lock failed");
620      }
621  }
622
623  void JSBridgeObject::RunJavaScriptCallback(const char *result) {
624      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
625                   "JSBridgeObject OH_NativeArkWeb_RunJavaScript result:%{public}s", result);
626  }
627
628  void JSBridgeObject::ProxyMethod1(const ArkWeb_JavaScriptBridgeData *dataArray, int32_t arraySize) {
629      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "JSBridgeObject ProxyMethod1 argc:%{public}d",
630                   arraySize);
631      for (int i = 0; i < arraySize; i++) {
632          std::string result((char *)dataArray[i].buffer, dataArray[i].size);
633          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
634                       "JSBridgeObject ProxyMethod1 argv[%{public}d]:%{public}s, size:%{public}d", i, result.c_str(),
635                       dataArray[i].size);
636      }
637  }
638
639  void JSBridgeObject::ProxyMethod2(const ArkWeb_JavaScriptBridgeData *dataArray, int32_t arraySize) {
640      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "JSBridgeObject ProxyMethod2 argc:%{public}d",
641                   arraySize);
642      for (int i = 0; i < arraySize; i++) {
643          std::string result((char *)dataArray[i].buffer, dataArray[i].size);
644          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
645                       "JSBridgeObject ProxyMethod2 argv[%{public}d]:%{public}s, size:%{public}d", i, result.c_str(),
646                       dataArray[i].size);
647      }
648  }
649
650  void JSBridgeObject::SaySomething(const char *say) {
651      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "JSBridgeObject SaySomething argc:%{public}s", say);
652  }
653  ```
654