鸿蒙原生系列之ArkWeb技能提升——H5调用应用侧API

H5调用应用侧API

  • 〇、前言
  • [一、ArkWeb 属性:javaScriptProxy](#一、ArkWeb 属性:javaScriptProxy)
  • 二、开启H5调试支持
    • 1、申请联网权限
    • [2、Web 组件开启 Debug 模式](#2、Web 组件开启 Debug 模式)
    • [3、设置 IDE 支持 Auto WebView Debug](#3、设置 IDE 支持 Auto WebView Debug)
  • [三、javaScriptProxy 使用示例](#三、javaScriptProxy 使用示例)

〇、前言

在分布式架构成为主流的今天,APP的页面不一定都是原生代码实现的,还有可能是存放在云端的H5页面,特别是在追求跨平台一致性的应用场景中。而通过云端H5实现的页面,一些交互的实现,通常又会调用到APP内部的相关API,那么这种调用在鸿蒙系统的WebView中,是否支持呢?又该如何实现呢?

一、ArkWeb 属性:javaScriptProxy

在用于网页显示的 Web 组件中,有一个具有特殊功能的属性:javaScriptProxy。proxy 这一单词,通常与代理 有关,而在 ArkWeb 中,javaScriptProxy 也是起到一种代理的作用。

仔细阅读该属性的官方介绍,就会发现它便是鸿蒙应用开发中,实现H5页面调用ArkTS API的关键 。同时,也不难注意到,除了用 Web 组件的属性外,还可以用 registerJavaScriptProxy 这个 API 方法去实现相同的目的,而 registerJavaScriptProxy 可以通过 webview.WebviewController 的实例对象去访问。

javaScriptProxy 和 registerJavaScriptProxy 的区别,在于:javaScriptProxy可以在 Web 组件初始化的时候调用,而 registerJavaScriptProxy 需要在 Web 组件初始化完成后进行调用,并且使用registerJavaScriptProxy()方法注册的ArkTS对象,在下次加载或者重新加载后才生效。 因此,我是比较推荐使用 javaScriptProxy 属性的。

二、开启H5调试支持

默认情况下,鸿蒙APP页面即便用到了Web组件、加载了 H5 页面,也是无法在电脑上用edge等浏览器进行 H5 页面调试的,要支持H5页面的调试,需要通过以下步骤进行专门的设置。

1、申请联网权限

即便APP所使用的 H5 页面,就放在应用工程目录中,换句话说,即便你的APP完全用不到网络,但是为了支持 H5 调试,还是必须申请一个网络权限。在鸿蒙应用中,网络权限是非受限权限,权限的申请很简单,就是在 entry 模块的 module.json5 中添加如图所示的代码:

2、Web 组件开启 Debug 模式

Web 组件默认是不支持 Debug 也就是远程调试的,这是为了应用的安全,而如果需要 Web 组件支持调试,就必须手动打开 Debug 模式,而这就是第二步。

通常,可以在 Web 组件所在的页面的生命周期函数 aboutToAppear 中,将 Web 组件的调试模式打开:

typescript 复制代码
aboutToAppear() {
  // 配置Web开启调试模式
  webview.WebviewController.setWebDebuggingAccess(true);
}

3、设置 IDE 支持 Auto WebView Debug

最后一步,就是设置 DevEco Studio 本身:

打开运行/调试配置页,将如图的选项勾选。勾选了 『Auto WebView Debug』这一运行选项后,下次运行工程,在运行窗口就会看到打印如下一行提示:

而当APP进入用到了 Web 组件的页面时,就会看到打印这样一行提示:

这时候,用电脑浏览器,例如 Edge 浏览器,在网址输入框中输入 edge://inspect/#devices 就可以看到 APP 的 H5 页面已经被 Inspect 到:

而点击 inspect 便可以进入调试界面:

三、javaScriptProxy 使用示例

下面,开始介绍如何使用 javaScriptProxy 为 H5 页面提供 ArkTS API。

1、准备一个 ArkTS 对象

javaScriptProxy 并不支持直接注册某个 Page 结构体内部的成员方法,只支持注册非 Page 的 ArkTS 对象,所以,不妨先准备如下一批 ArkTS 类:

javascript 复制代码
class Student {
  name: string = '';
  age: string = '';
}

class ObjOther {
  methodNameListForJsProxy: string[]

  constructor(list: string[]) {
    this.methodNameListForJsProxy = list
  }

  testOther(json: string): void {
    console.info(json)
  }
}

class TestClass {
  ObjReturn: ObjOther
  constructor() {
    this.ObjReturn = new ObjOther(["testOther"]);
  }

  test(): string {
    return 'ArkTS Hello World!';
  }

  toString(): string {
    return "演示registerJavaScriptProxy()接口注册"
  }

  deliverArray(): Array<number> {
    return [1, 2, 3, 4, 5];
  }

  deliverObject(): Student {
    let student = new Student();
    student.name = "张三";
    student.age = "18";
    return student;
  }

  callH5Func(param: Function): void {
    param("ArkTS Call H5 Callback Function")
  }

  bridgeMessage(message: string): void {
    console.log("H5 message: " + message);
  }

  sayHello(param1: ESObject, param2: ESObject): void {
    param1.hello("调用方式1");
    param2.hello("调用方式2");
  }

  callObjFunc(): ESObject {
    return this.ObjReturn;
  }

  callPromise(): Promise<string> {
    let promise: Promise<string> = new Promise<string>((resolve, reject) => {
      try {
        resolve("ArkTS Promise Resolve");
      } catch (error) {
        reject("ArkTS Promise Reject");
      }
    });
    return promise;
  }

  receivePromise(resolve: Function): void {
    setTimeout( () => { resolve("suc") }, 10000)
  }

}

2、注册 ArkTS 对象

接着,将准备好的 ArkTS 对象,通过 javaScriptProxy 注册到 Web 容器中:

javascript 复制代码
@Entry
@Component
struct WebPage {
  webviewController: webview.WebviewController = new webview.WebviewController();
  // 声明需要注册的对象
  @State testObj: TestClass = new TestClass();

  aboutToAppear() {
    // 配置Web开启调试模式
    webview.WebviewController.setWebDebuggingAccess(true);
  }

  aboutToDisappear(): void {
    try {
      this.webviewController.deleteJavaScriptRegister("testObjName");
    } catch (error) {
      const err = error as BusinessError<string>;
      console.log("发生错误:" + err.message)
    }
  }

  build() {
    Column({space: 15}){

      Text("演示前端页面调用应用侧函数")
      .fontSize(20)
      .fontColor(Color.Black)
      .fontWeight(FontWeight.Bold)

      Button("Back")
        .height(80)
        .width("30%")
        .backgroundColor(Color.Black)
        .fontColor(Color.White)
        .onClick(() => {
          router.back()
        })
      
      Web({ src: $rawfile('index.html'), controller: this.webviewController})
      // 将对象注入到web端
        .javaScriptProxy({
          object: this.testObj,
          name: "testObjName",
          methodList: ["test", "toString", "bridgeMessage", "deliverArray", "deliverObject", "callH5Func", "sayHello", "callObjFunc", "callPromise", "receivePromise"],
          controller: this.webviewController,
          // 可选参数
          asyncMethodList: [],
          permission: '{"javascriptProxyPermission":{"urlPermissionList":[{"scheme":"resource","host":"rawfile","port":"","path":""},' +
            '{"scheme":"e","host":"f","port":"g","path":"h"}],"methodList":[{"methodName":"test","urlPermissionList":' +
            '[{"scheme":"https","host":"xxx.com","port":"","path":""},{"scheme":"resource","host":"rawfile","port":"","path":""}]},' +
            '{"methodName":"test11","urlPermissionList":[{"scheme":"q","host":"r","port":"","path":"t"},' +
            '{"scheme":"u","host":"v","port":"","path":""}]}]}}'
        })
    }
  }

}

javaScriptProxy 的参数有如下:

3、编写 index.html

在 Entry 模块的 rawfile 目录中新建一个 html 文件,并编写如下内容

html 复制代码
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<p id="showArr"></p>
<p id="showObj"></p>
<p id="hello1"></p>
<p id="hello2"></p>
<p id="showPromise"></p>
<p id="receivePromise"></p>
<script>
    // 写法一
    class Student {
        constructor(nameList) {
            this.methodNameListForJsProxy = nameList;
        }
        hello(param) {
            document.getElementById("hello1").innerHTML = "方式1:" + param;
        }
    }
    var st = new Student(["hello"])
    // 写法2
    //创建一个构造器,构造函数首字母大写
    function Obj1(){
        this.methodNameListForJsProxy=["hello"];
        this.hello=function(param){
            document.getElementById("hello2").innerHTML = "方式2:" + param;
        };
    }
    //利用构造器,通过new关键字生成对象
    var st1 = new Obj1();
    function callArkTS() {
        let str = testObjName.test();
        document.getElementById("demo").innerHTML = str;
        console.info('ArkTS Hello World! :' + str);
        let str2 = testObjName.toString();
        console.info('ArkTS toString() :' + str2);
        let arr = testObjName.deliverArray();
        document.getElementById("showArr").innerHTML = arr;
        let obj = testObjName.deliverObject();
        let obj_str = "姓名:" + obj.name + ",年龄:" + obj.age;
        document.getElementById("showObj").innerHTML = obj_str;
        testObjName.callH5Func(function(param){testObjName.bridgeMessage(param)});
        testObjName.sayHello(st, st1);
        testObjName.callObjFunc().testOther("前端页面调用应用侧Object里的Function");
        testObjName.callPromise().then((param) => {
            let msg = "Promise成功:" + param;
            document.getElementById("showPromise").innerHTML = msg;
        }).catch((param) => {
            let msg = "Promise失败:" + param;
            document.getElementById("showPromise").innerHTML = msg;
        })
        let funPromise;
        var promise = new Promise(function(resolve, reject){
            funPromise=(param) => {resolve(param)};
        });
        testObjName.receivePromise(funPromise);
        promise.then((param) => {
            let msg = "Promise成功:" + param;
            testObjName.bridgeMessage(msg)
            document.getElementById("receivePromise").innerHTML = msg;
        })
    }
</script>
</body>
</html>

4、支持传递的数据类型或方法类型

在上面的案例中,借助 Web 组件的 javaScriptProxy 属性,将一组数据和方法,从 ArkTS 传递到 H5 页面中,具体可划分为如下几大类:

1)简单的方法调用,如 testObjName.test() 和 testObjName.toString();

2)传递数组:testObjName.deliverArray()

3)传递对象:testObjName.deliverObject()

4)传递 callback 方法:testObjName.callH5Func(function(param){testObjName.bridgeMessage(param)})

5)传递 H5 里的对象及其成员方法:

javascript 复制代码
// 写法一
class Student {
    constructor(nameList) {
        this.methodNameListForJsProxy = nameList;
    }
    hello(param) {
        document.getElementById("hello1").innerHTML = "方式1:" + param;
    }
}
var st = new Student(["hello"])
// 写法2
//创建一个构造器,构造函数首字母大写
function Obj1(){
    this.methodNameListForJsProxy=["hello"];
    this.hello=function(param){
        document.getElementById("hello2").innerHTML = "方式2:" + param;
    };
}
// ......
testObjName.sayHello(st, st1);

6)传递应用侧 Object 里面的 Function:testObjName.callObjFunc().testOther("前端页面调用应用侧Object里的Function");

7)传递 promise 方法

javascript 复制代码
testObjName.callPromise().then((param) => {
     let msg = "Promise成功:" + param;
     document.getElementById("showPromise").innerHTML = msg;
 }).catch((param) => {
     let msg = "Promise失败:" + param;
     document.getElementById("showPromise").innerHTML = msg;
 })
 let funPromise;
 var promise = new Promise(function(resolve, reject){
     funPromise=(param) => {resolve(param)};
 });
 testObjName.receivePromise(funPromise);
 promise.then((param) => {
     let msg = "Promise成功:" + param;
     testObjName.bridgeMessage(msg)
     document.getElementById("receivePromise").innerHTML = msg;
 })

最终效果如下:

相关推荐
YJlio29 分钟前
[鸿蒙2025领航者闯关] 基于鸿蒙 6 的「隐私感知跨设备办公助手」实战:星盾安全 + AI防窥 + 方舟引擎优化全流程复盘
人工智能·安全·harmonyos
御承扬29 分钟前
鸿蒙原生系列之监听布局和送显事件
harmonyos·鸿蒙ndk ui
食品一少年32 分钟前
【Day7-10】开源鸿蒙Flutter 常用组件封装实战(2)
flutter·华为·harmonyos
赵财猫._.7 小时前
鸿蒙分布式安全通信:跨设备身份认证与数据加密传输
分布式·安全·harmonyos
ゞ 正在缓冲99%…8 小时前
2025.9.28华为软开
算法·华为
Raink9 小时前
HarmonyOS应用开发基础案例(一):鸿蒙页面布局入门
harmonyos
Raink9 小时前
HarmonyOS 应用开发基础案例(三):使用DevEco Studio高效开发(篇一)
harmonyos
灰灰勇闯IT10 小时前
KMP算法在鸿蒙系统中的应用:从字符串匹配到高效系统级开发(附实战代码)
算法·华为·harmonyos
灰灰勇闯IT11 小时前
Flutter×鸿蒙深度融合指南:从跨端适配到分布式能力落地(2025最新实战)
分布式·flutter·harmonyos