背景介绍

业务背景

2024年1月18日华为宣布:HarmonyOS NEXT 将不再支持 Android系统,基于以上背景及国内信贷业务现状,公司决定启动借款App鸿蒙化项目。

下图是2024年6月华为HDC大会上,华为宣布 HarmonyOS NEXT 将面向开发者和先锋用户启动Beta升级,并计划在今年四季度正式商用。
image.png

技术背景

借款APP业务层约70%是基于H5页面构建的,经过多年的迭代,Android与iOS已经有一个非常稳定和强大的Web组件,即:PPDWebUI。为了避免业务层过多地适配,加快App鸿蒙化进程,在技术上只需要开发一个鸿蒙版的PPDWebUI,功能和Android、iOS保持一致,就可以承接已有业务。最终使得业务站点改动的成本最小,并且实现了三端技术架构的统一。
下面的内容主要是介绍我们实现PPDWebUI的方案和实现过程中遇到的困难与挑战。

方案设计
image.png

如上图,方案的思路是基于鸿蒙官方提供的ArkWeb进行封装,实现鸿蒙PPDWebUI组件
• ArkWeb简介:鸿蒙官方提供的基于谷歌Chromium内核的Web组件,使用的Chromium版本为M114(截止本文编写时)
PPDWebUI组件分为两部分
• Web容器,提供H5容器和原生服务
• ppdwebui.js,负责JS与Native的通信,本质上是对JsBrdige的封装

PPDWebUI组件的Web容器
•原生和H5交互原理

  1. 使用JsBridge通信通过javaScriptProxy和runJavaScript来实现JsBridge。使用javaScriptProxy将原生侧接口注入到H5的window对象上,通过runJavaScript接口执行JS脚本到H5中,并在回调中获取脚本执行结果。

•调用流程如下图所示:
image.png

•代码示例:
Web({ src: this.url ?? '', controller: this.webviewController }).javaScriptProxy({ // 将对象注入到web端 object: this.jsApi, name: "_foo", methodList: ["bar"], controller: this.webviewController })

•注入原生服务不采用开始加载页面的时候就注入原生服务的原因:
•减少不必要的注入,每次注入原生服务都需要依赖于站点应用引用的js。
•更安全可控,每次注入原生服务的时候都可以进行安全校验。通过ArkWeb中的onInterceptRequest方法拦截js替换本地WebUI.js,同时注入原生的插件方法名称,让其挂载在容器上,这样H5就可以调用原生方法,拦截注入代码如下:

onInterceptRequest(event?: InterceptEvent): WebResourceResponse | null { let url: string = event?.request?.getRequestUrl() as string; if (url.match(/^https?://xxx.xxx.com/[\d.]+/xxx.js$/)) { let webResp = new WebResourceResponse(); const resourceMgr: resourceManager.ResourceManager = this.context.resourceManager; let js = buffer.from(resourceMgr.getRawFileContentSync('xxx1.0.js')).toString('utf-8') let respData = ${js};window.PPDWebUI && window.PPDWebUI.initJSConfig && window.PPDWebUI.initJSConfig(${this.appendJsStr}) webResp.setResponseData(respData); webResp.setResponseCode(200) webResp.setResponseEncoding('utf-8') webResp.setResponseMimeType('application/javascript') webResp.setResponseIsReady(true) return webResp } else { return null } }

image.png

• PPDWebUI容器使用

  1. 容器初始化

// 页面即将显示的时候注册服务aboutToAppear(): void { PPDWebUIInitManager.registerService(new LoanServiceCaller());}//自定义WebUI组件初始化WebUI({ webUrl: this.webUrl, props: this.webuiPro, webClient: new WebClientImp(getContext(this)), webUIConfig: new WebUIConfig(this.appendUserAgent,true), webUIHostController: this.webUIHostController}) .layoutWeight(1) .width('100%')
• H5业务的适配

  1. 升级WebUI.js:所有业务的H5页面均使用统一提供的PPDWebUI.js调用Native的原生能力,H5页面仅需要对 PPDWebUI.js 进行升级,即可以运行在鸿蒙系统上,无需其他特殊适配逻辑。
  2. 如有需要针对操作系统的特殊需求,页面可依据Native注入的系统信息来识别当前运行的操作系统,简单高效。

困难与挑战
下面列举了我们在实现鸿蒙版PPDWebUI组件时遇到的问题与解决方案,供大家参考

  1. H5页面与原生容器如何联调?
    • ArkWeb组件支持使用DevTools工具调试前端页面,开发者需通过setWebDebuggingAccess()接口开启Web组件前端页面调试能力,利用DevTools工具可以在电脑上调试移动设备上的前端网页
    • 使用DevTools工具,可以执行以下步骤:
    • 在应用代码中开启Web调试开关,具体如下:

    aboutToAppear() { // 配置Web开启调试模式 webview.WebviewController.setWebDebuggingAccess(true); } build() { Column() { Web({ src: 'www.example.com', controller: this.controller }) } }}
    开启调试功能需要在DevEco Studio应用工程hap模块的module.json5文件中增加如下权限,添加方法请参考在配置文件中声明权限。

"requestPermissions":[ { "name" : "ohos.permission.INTERNET" } ]
将设备连接上电脑,在电脑端配置端口映射,配置方法如下:

//查找 devtools 远程调试所需的 domain socket 名称,该名称与进程号有关,重启调试应用后,需要重复此步骤,以完成端口转发cat /proc/net/unix | grep devtools// 添加映射 [pid] 替换成实际的进程idhdc fport tcp:9222 localabstract:webview_devtools_remote_[pid]// 查看映射 hdc fport ls示例:hdc shellcat /proc/net/unix | grep devtoolsexithdc fport tcp:9222 localabstract:webview_devtools_remote_3458hdc fport ls
在电脑端Chrome浏览器地址栏中输入chrome://inspect/#devices,页面识别到设备后,就可以开始页面调试。调试效果如下:

image.png

  1. 导航栏标题不显示或显示不对问题?
    • 通过H5容器的ArkWeb的原生onTitleReceive方法获取H5的docment.title,设置导航标题
    Web(...) .onTitleReceive((event) => { this.props.navTitle = event.title; })
  2. H5页面需支持侧滑返回上一次H5页面问题?
    • 通过H5容器的ArkWeb的原生onBackPressed方法监听返回键,拦截返回动作,实现H5页面的侧滑返回上一页。
  3. 同一个webview中,先打开A页面,然后setTimeout 2秒 后使用location.href跳转B页面。B页面没有触发webview的任何事件问题?
    • 原因是调用setCustomUserAgent后与web页面的跳转时序相关,Web跳转后才设置UserAgent,这就导致页面跳转了但新UserAgent关联的页面堆栈数仍只有一个WebView
  4. 多个JsBridge注入问题?
    • 无法使用 javaScriptProxy 方法注入多个 JsBridg对象,需要在生命周期方法 onControllerAttached() 中 调用 WebViewController 的 registerJavaScriptProxy() 方法注入 JsBridg对象
    Web(...) .onControllerAttached(() => { this.webviewController.registerJavaScriptProxy(...) })

未来规划
• 在业务迭代过程中,为进一步抹平三端差异,计划将Android/iOS离线包方案也移植到鸿蒙版借款App中,提升H5的用户体验
• 性能优化,开发过程中,也面临ArkWeb加载和丢帧等问题,目前了解到DevEco Profiler提供ArkWeb分析模板,后面也会结合ArkWeb执行流程的关键trace点,来定位问题发生的阶段,并做一些针对性的性能优化

作者简介
Jsiguo,信也科技移动研发资深专家


信也科技布道师
12 声望10 粉丝