Harmonyos之加载本地web页面
- 本地文件存放位置
- resources资源文件下的rawfile目录
- resources资源文件下的自定义目录
- Native和H5的交互
- 通过端口通信技术
- Native代码
- H5代码
- h5调用Native方法(对象注册)
- javaScriptProxy(Native注册对象)
- registerJavaScriptProxy(Native注册对象)
- deleteJavaScriptRegister(Native删除注册对象)
- h5端调用注册对象的方法
- Native调用H5的方法
- H5侧代码
- Native侧
- 参考链接
本地文件存放位置
resources资源文件下的rawfile目录
第一种是放在src\main\resources\rawfile
文件夹下,在ets文件中通过$rawfile('文件名')访问
。
Web({ src: $rawfile('test.html'), controller: this.controller})
resources资源文件下的自定义目录
我们也可以把h5文件访问到src\main\resources\
目录下的自定目录
加载方式:
// 获取本地h5的资源路径,可以凭借h5链接需要的参数@State url: string = "file://" + getContext().resourceDir + "/uatfile/index.html#/"+"?accessToken="+this.acct+"&userName="+this.user_nameWeb({ src: '', controller: this.controller }).onlineImageAccess(true).imageAccess(true).darkMode(this.mode).domStorageAccess(true)//开启文档对象模型存储接口.javaScriptAccess(true)//允许执行JavaScript脚本.javaScriptProxy({object: new HippiusBridge(this.controller),name: "xxxx",methodList: ["postMessage"],controller: this.controller}).fileAccess(true)//开启应用中文件系统的访问.mixedMode(MixedMode.All)//允许加载超文本传输协议(HTTP)和超文本传输安全协议(HTTPS)混合内容.focusOnTouch(false).metaViewport(true)//设置meta标签的viewport属性是否可用.onControllerAttached(() => {//当Controller成功绑定到Web组件时触发该回调// 推荐在此loadUrl、设置自定义用户代理、注入JS对象等if (this.url) {console.info('[ ~~ mwebview onControllerAttached~~ ] : ' +this.url.toString());this.controller.setPathAllowingUniversalAccess([getContext().resourceDir,getContext().resourceDir+"/uatfile"])try {let userAgent = this.controller.getUserAgent() + this.customUserAgent;this.controller.setCustomUserAgent(userAgent)} catch (error) {console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);}this.controller.loadUrl(this.url)}this.initBridge()}).onOverrideUrlLoading((webResourceRequest: WebResourceRequest) => {//当空白页则拦截 加载自定义空白页面if (webResourceRequest && webResourceRequest.getRequestUrl() == "about:blank") {return true;}return false;}).onPageBegin((event) => {if (event) {console.log('onPageBegin url:' + event.url);}}).onLoadIntercept((event) => {// 通过url检测是否点击打电话if (event.data.getRequestUrl().startsWith('tel')) {this.pushTelPage(event.data.getRequestUrl())}return false}).onProgressChange((event) => {if (event) {console.log('newProgress:' + event.newProgress);}}).onPageEnd((event) => {// 推荐在此事件中执行JavaScript脚本if (event) {console.log('onPageEnd url:' + event.url);}}).geolocationAccess(true)
Native和H5的交互
通过端口通信技术
前端页面和Native之间可以用createWebMessagePorts()
接口创建消息端口来实现两端的通信
Native代码
创建通道:
ports: webview.WebMessagePort[] = [];
// 1、创建两个消息端口。this.ports = this.controller.createWebMessagePorts();
在Native侧消息端口(如端口1)注册 回调事件:
// 2、在应用侧的消息端口(如端口1)上注册回调事件。this.ports[1].onMessageEvent((result: webview.WebMessage) => {// 注册回调事件, 监听H5发送过来的消息// type WebMessage = ArrayBuffer | string;// result代表h5传递过来的结果,支持上述类型的数据})
将另一个消息端口(如端口0)发送到HTML侧,由HTML侧保存并使用。
// 创建web控制器方法
controller: webview.WebviewController = new webview.WebviewController();this.controller.postMessage('__init_port__', [this.ports[0]], '*');
使用Native的端口给传递给h5的端口发送消息:
// 发送消息给H5
this.ports[1].postMessageEvent(this.sendFromEts);
H5代码
监听window上的message
方法,接受Native传递过来的端口:
var h5Port;
window.addEventListener('message', function (event) {
// 接收端if (event.data === '__init_port__') {if (event.ports[0] !== null) {h5Port = event.ports[0]; // 1. 保存从应用侧发送过来的端口。h5Port.onmessage = function (event) {// onmessage 方法是监听Native端发过来的消息}}}})
h5段通过端口给Native发送消息:
// 使用h5Port向Native发送消息。
function PostMsgToEts(data) {if (h5Port) {h5Port.postMessage(data);} else {console.error('h5Port is null, Please initialize first');}
}
h5调用Native方法(对象注册)
javaScriptProxy(Native注册对象)
在Web组件初始化的时候调用javaScriptProxy()
方法,给h5注册对象
注入JavaScript
对象到window对象
中,并在window对象中调用该对象的方法
。所有参数不支持更新。注册对象时,同步与异步方法列表请至少选择一项不为空,可同时注册两类方法。同一方法在同步与异步列表中重复注册,将默认异步调用。
创建一个注册的类
class testClass {constructor() {}test(): string {return 'ArkTS Hello World!';}
}
注册对象:
class testClass {constructor() {}test(): string {return "ArkUI Web Component";}toString(): void {console.log('Web Component toString');}
}// 将对象注入到web端.javaScriptProxy({object: this.testObj, // 需要注册的对象name: "testObjName",// windows对象上的对象methodList: ["test"],// 注册对象中的方法列表controller: this.webviewController,// 可选参数asyncMethodList: [],permission: ''})
registerJavaScriptProxy(Native注册对象)
在Web组件初始化完成后调用registerJavaScriptProxy()
方法来注册
// 这个是在组件加载完成后, 你可以在生命周期方法中
this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"],// 可选参数, asyncMethodList[],// 可选参数, permission'')使用registerJavaScriptProxy()接口注册方法时,注册后需调用refresh()接口生效。
deleteJavaScriptRegister(Native删除注册对象)
上述两种方式都需要和deleteJavaScriptRegister
方法来配合使用, 防止内存泄漏。
try {this.webviewController.deleteJavaScriptRegister("testObjName");} catch (error) {console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);}
h5端调用注册对象的方法
// 直接获取Native注册到windows的对象testObjName, 然后调用其方法
let str = testObjName.test();
Native调用H5的方法
H5侧代码
// 调用有参函数时实现。
function htmlTest(param) {// 有参数数实现方法
}
// 调用无参函数时实现。
function htmlTest() {// 无参函数实现方法
}
//上述方法前面实际省略了windwos. , 这个两个方法中实际上住注册在window对象中的。
Native侧
// 前端页面函数无参时,将param删除。
this.webviewController.runJavaScript('htmlTest(param)');
在实际开发过程中我们其实可以灵活实现, 我们在H5可把回调方法传递给Native侧, 然后Native侧得到结果之后再调用回调方法。
// 获取app版本信息window.getAppInfoSuccess = this.getOhosAppVersion.bind(this);const dict = {'className': 'AppVersionBridgePlugin','function': 'getVersionNumber','successCallBack': 'getAppInfoSuccess','failureCallBack': '',};// Native侧注册一个公共对象HandBridge, 然后该对象中有一个公共方法,postMessage, 前端统一调用该对象的这个方法, Native收到这个对象在去分发到每个具体的功能实现类中
HandBridge.postMessage(JSON.stringify(dict))
HandBridge方法实现:
postMessage(data: string) {if (data) {let obj = JSON.parse(data) as object //获取js传递过来的数据if (obj) {let className = obj["className"] as stringlet functionName = obj["function"] as stringlet params = obj["params"] as objectlet successCallBack = obj["successCallBack"] as stringlet failCallBack = obj["failureCallBack"] as stringlet callbackContext =new CallbackContext(successCallBack, failCallBack, this.controller)if (this.plugins.get(className)) {//调用原生方法this.exec(this.plugins.get(className), functionName, params, callbackContext)return}import(`../plugin/${className}`).then((ns: ESObject) => {let classObj: ESObject = new ns[className](); // 实例化if (classObj instanceof HippiusPlugin) {this.plugins.set(className, classObj)this.exec(classObj, functionName, params, callbackContext)}})}}}private exec(plugin: HippiusPlugin, action: string, params: ESObject, callback: CallbackContext) {if (plugin) {plugin.execute(action, params, callback)}}
CallbackContext类实现:
export class CallbackContext {static readonly TAG = 'CallbackContext'mSuccess: stringmError: stringcontroller: webview.WebviewControllerconstructor(mSuccess: string, mError: string, controller: webview.WebviewController) {this.mSuccess = mSuccessthis.mError = mErrorthis.controller = controller}public onSuccess(args: string) {this.toJS(this.mSuccess, args)}public onError(args: string) {this.toJS(this.mError, args)}private toJS(fun: string, args: string) {if (args) {args = args.replace("'", "\\'")}let js = `${fun}('${args}')`if (this.controller) {this.controller.runJavaScript(js)}}
}
参考链接
更多Native和H5交互资料参考链接:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/web-use-frontend-page-js