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 使用示例)
-
- [1、准备一个 ArkTS 对象](#1、准备一个 ArkTS 对象)
- [2、注册 ArkTS 对象](#2、注册 ArkTS 对象)
- [3、编写 index.html](#3、编写 index.html)
- 4、支持传递的数据类型或方法类型
〇、前言
在分布式架构成为主流的今天,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;
})
最终效果如下:

