Harmonyos之加载本地web页面
本地文件存放位置
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_name
Web({ 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 string
let functionName = obj["function"] as string
let params = obj["params"] as object
let successCallBack = obj["successCallBack"] as string
let failCallBack = obj["failureCallBack"] as string
let 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: string
mError: string
controller: webview.WebviewController
constructor(mSuccess: string, mError: string, controller: webview.WebviewController) {
this.mSuccess = mSuccess
this.mError = mError
this.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