【鸿蒙开发】 使用ArkWeb组件加载H5页面

1. ArkWeb

相当于iOS/Android中的WebView控件,给个url就可以展示H5页面。

但是在项目中,我们需要考虑以下需求:

  • H5页面加载(处理页面加载的状态:成功、失败、加载中)
  • H5页面栈(window.history)管理
  • UserAgent管理
  • Cookie管理
  • Native和H5交互
  • 页面加载优化
  • Web页面调试

我也是带着这些问题去学习使用ArkWeb的。

2. 使用ArkWeb加载url

页面加载是Web组件最基本的功能了,支持三种场景:

  • 加载网络页面(需要配置ohos.permission.INTERNET网络访问权限);
  • 加载本地页面;
  • 加载HTML格式的富文本数据;

我们以加载网络页面为例:

// 导入webview
import { webview } from '@kit.ArkWeb';

@Entry
@Component
struct Index {

  private webviewController = new webview.WebviewController()

  build() {
    Stack() {
      Web({ src: "https://www.baidu.com", controller: this.webviewController })
        .onPageBegin((event) => {
          // 网页开始加载时触发该回调
        })
        .onPageEnd((event) => {
          // 网页加载完成时触发该回调
        })
        .onErrorReceive((event) => {
          // 网页加载遇到错误时触发该回调. 无网下会触发该回调
        })
        .onHttpErrorReceive((event) => {
          // 网页加载资源遇到的HTTP错误(响应码>=400)时触发该回调
        })
        .onProgressChange((event) => {
          // 网页加载进度变化时触发该回调
        })
    }
  }
}

Web组件暴露了很多回调方法,我们可以在跟加载进度和状态的回调方法中设置webview在加载中、加载失败、加载成功的样式。

3. H5页面栈(window.history)管理

在上面的示例中,可以看到在创建Web组件的时候,传入了一个controller。我们没办法直接操作Web组件,只能通过controller去操作Web组件的行为,包括页面栈管理、页面刷新、执行JS等。

我们先来看下controller提供的管理页面栈的方法,如下所示:

// 判断当前页面是否可前进,是否有前进历史记录
accessForward(): boolean

// 判断页面是否可后退,即当前页面是否有返回历史记录
accessBackward(): boolean

// 按照历史栈,前进一个页面
forward(): void

// 按照历史栈,后退一个页面
backward(): void

// 删除所有前进后退记录
clearHistory(): void

4. UserAgent管理

默认的UserAgent定义如下: Mozilla/5.0 ({deviceType}; {OSName} {OSVersion}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/{ArkWeb VersionCode} {Mobile}

截屏2024-04-09 18.25.01.png

举例:

Mozilla/5.0 (Phone; OpenHarmony 4.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile

4.1 在webInited回调设置UA

目前在Preview1版本测试webInited对应的回调方法不会执行,等升级后再进行验证。🙋🙋🙋

如果我们想基于默认的UserAgent去定制UserAgent:

// xxx.ets
import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';

@Entry
@Component
struct WebComponent {
  controller: web_webview.WebviewController = new web_webview.WebviewController();
  @State ua: string = ""

  aboutToAppear():void {
    web_webview.once('webInited', () => {
      try {
        // 应用侧用法示例,定制UserAgent。
        this.ua = this.controller.getUserAgent() + 'xxx';
      } catch(error) {
        let e:business_error.BusinessError = error as business_error.BusinessError;
        console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
      }
    })
  }

  build() {
    Column() {
      Web({ src: 'www.example.com', controller: this.controller })
        .userAgent(this.ua)
    }
  }
}

上面的代码是在webInited回调中,通过this.controller.getUserAgent()获取默认的UA。once是webview提供的方法,用于订阅一次指定类型Web事件的回调,目前也仅支持webInited(Web初始化完成)事件。

4.2 在onControllerAttached回调中设置UA

controller中可以通过setCustomUserAgent方法来设置自定义用户代理,会覆盖系统的用户代理,推荐放在onControllerAttached回调中。

若需要setCustomUserAgent,在setCustomUserAgent方法后执行this.controller.loadUrl(this.webUrl),webUrl为要加载的web页面,在原始的web组件的src可以设置一个空字符串。

import web_webview from '@ohos.web.webview'

@Entry
@Component
struct WebComponent {
  controller: web_webview.WebviewController = new web_webview.WebviewController();

  build() {
    Column() {
      // 这个src可以先设置为空的字符串
      Web({ src: '', controller: this.controller })
        .onControllerAttached(()=>{
          let userAgent = this.controller.getUserAgent() + "xxx";
          this.controller.setCustomUserAgent(userAgent);
          // 设置完UA之后,再次加载url
          this.controller.loadUrl("www.baidu.com")
        })
    }
  }
}

5. Cookie管理

Web组件提供了WebCookieManager类,用来管理Web组件的Cookie信息,Cookie信息保存在应用沙箱路径下/proc/{pid}/root/data/storage/el2/base/cache/web/Cookiesd的文件中。

下面以configCookieSync接口举例,为 www.example.com设置单个Cookie的值:

// xxx.ets
import web_webview from '@ohos.web.webview';
import business_error from '@ohos.base';

@Entry
@Component
struct WebComponent {
  controller: web_webview.WebviewController = new web_webview.WebviewController();

  build() {
    Column() {
      Button('configCookieSync')
        .onClick(() => {
          try {
            web_webview.WebCookieManager.configCookieSync('https://www.example.com', 'value=test');
          } catch (error) {
            let e: business_error.BusinessError = error as business_error.BusinessError;
            console.error(`ErrorCode: ${e.code},  Message: ${e.message}`);
          }
        })
      Web({ src: 'www.example.com', controller: this.controller })
    }
  }
}

WebCookieManager还提供了其他操作Cookie的方法,截图如下:

在这里插入图片描述

6. Native和H5交互

6.1 Native调用H5页面中的函数

Native可以通过runJavaScript()方法执行JavaScript,调用前端页面的JavaScript相关函数。

H5页面:

<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<script>
    var param = "JavaScript Hello World!";
    function htmlTest(param) {
        console.log(param);
    }
</script>
</body>
</html>

Native页面:

// xxx.ets
import web_webview from '@ohos.web.webview';

@Entry
@Component
struct WebComponent {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();

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

  build() {
    Column() {
      Button('runJavaScript')
        .onClick(() => {
          // 通过调用webviewController的runJavaScript方法执行js函数
          this.webviewController.runJavaScript('htmlTest(param)');
        })
      Web({ src: $rawfile('index.html'), controller: this.webviewController})
    }
  }
}

6.2 H5页面调用Native函数

Native需要先将应用侧代码注册到H5页面中,之后H5页面就可以使用注册的对象名称调用应用侧的函数,实现在H5页面中调用Native的方法。

有两种注册方式:

  • Web组件初始化调用,使用javaScriptProxy接口;
  • Web组件初始化完成后调用,使用registerJavaScriptProxy接口;

只是注册时机不同,其它都一样。这里以Web组件初始化调用为例,在Web组件初始化时进行注册:

// xxx.ets
import web_webview from '@ohos.web.webview';

// 用于注入的对象
class testClass {

  constructor() {
  }

  // H5页面要调用的方法
  test(): string {
    return 'ArkTS Hello World!';
  }
}

@Entry
@Component
struct WebComponent {
  webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  // 声明需要注册的对象
  @State testObj: testClass = new testClass();

  build() {
    Column() {
      // web组件加载本地index.html页面
      Web({ src: $rawfile('index.html'), controller: this.webviewController})
        // 将对象注入到web端
        .javaScriptProxy({
          object: this.testObj,
          name: "testObjName",  // H5会通过这个name调用方法
          methodList: ["test"], // 暴露有H5的方法
          controller: this.webviewController
        })
    }
  }
}

H5页面触发应用侧代码:

<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
    function callArkTS() {
        // testObjName是Native侧注册的名称
        // test是Native侧暴露的方法名称
        let str = testObjName.test();
        document.getElementById("demo").innerHTML = str;
        console.info('ArkTS Hello World! :' + str);
    }
</script>
</body>
</html>

双方可以互相通信之后,就可以根据业务需要进行封装和分发事件了。

6.3 兼容DSBridge

当前项目iOS/Android和H5通信用的是DSBridg这个库(当时项目时间紧,所以没考虑自己封装)。

DSBridge的实现原理还是挺简单的,通过H5页面调用prompt方法来向Native传递数据,数据中会包括:

  • H5页面的回调函数名
  • 调用Native的目标方法名
  • 调用Native的目标方法参数

Native会拦截系统Webview的prompt方法,解析数据,在执行完方法之后通过H5页面传过来的回调函数名执行H5的回调方法。

如果我们要做鸿蒙版本的App,就需要考虑对鸿蒙版本App的兼容,有两个选择:

  • 鸿蒙App中实现DSBridge的逻辑,这样H5侧不需要任何改动;
  • 基于鸿蒙Web组件提供的方法封装一套鸿蒙App特有的逻辑,这需要H5侧在通信层进行系统判断处理;

由于当时这个库只支持了API9,我们是基于API11进行开发和学习的,为了避免作者更新不及时,我们选择自己仿照这个库的逻辑进行实现。

主要是能拦截到H5侧DSBridge的方法调用,先来看下DSBridge在H5侧的实现:

if(window._dsbridge) {
    // 这个是关键代码
    ret = _dsbridge.call(method, arg)
}else if(window._dswk || navigator.userAgent.indexOf("_dsbridge") != -1){
    ret = prompt("_dsbridge=" + method, arg);
}

_dsbridge.call(method, arg)是关键代码,跟上面6.2中H5调用Native的方法是很相似的。

所以,我们可以在Native侧对其进行注册:

import web_webview from '@ohos.web.webview'

class JSBridge {

  // 暴露给H5的唯一的方法
  call(methodName: string, params: string): string {
    // 这里可以获取到methodName和params
    return ""
  }

}

@Entry
@Component
struct WebComponent {
  controller: web_webview.WebviewController = new web_webview.WebviewController();
  jsbridge: JSBridge = new JSBridge()

  build() {
    Column() {
      // 这个src可以先设置为空的字符串
      Web({ src: '', controller: this.controller })
        .onControllerAttached(()=>{
          let userAgent = this.controller.getUserAgent() + "xxx";
          this.controller.setCustomUserAgent(userAgent);
          // 设置完UA之后,再次加载url
          this.controller.loadUrl("www.baidu.com")
        })
        .javaScriptProxy({
          object: this.jsbridge,
          name: "_dsbridge", // _dsbridge是固定名字
          methodList: ["call"], // call也是固定方法名
          controller: this.controller
        })
    }
  }
}

这样DSBridge在H5页面就可以通过_dsbridge.call(method, arg)调用到Native的call方法了,然后Native在call方法中解析数据,按照iOS/Android的实现逻辑进行实现即可。

怎样学习鸿蒙?

首先必学的是开发语言 ArkTS,这是重中之重,然后就是ArkUI声明式UI开发、Stage模型、网络/数据库管理、分布式应用开发、进程间通信与线程间通信技术、OpenHarmony多媒体技术……。中间还有许多的知识点,都整理成思维导图来分享给大家~
在这里插入图片描述
此外,小编精心准备了一份联合鸿蒙官方发布笔记整理收纳的《鸿蒙开发学习笔记》,内容包含ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

有需要的小伙伴,可以扫描下方二维码免费领取!!!】

快速入门

  • 开发准备
  • 构建第一个ArkTS应用(Stage模型)
  • 构建第一个ArkTS应用(FA模型)
  • 构建第一个JS应用(FA模型)
    在这里插入图片描述

开发基础知识

  • 应用程序包基础知识
  • 应用配置文件(Stage模型)
  • 应用配置文件概述(FA模型)
    在这里插入图片描述

资源分类与访问

  • 资源分类与访问
  • 创建资源目录和资源文件
  • 资源访问
    在这里插入图片描述

学习ArkTs语言

  • 初识ArkTS语言
  • 基本语法
  • 状态管理
  • 其他状态管理
  • 渲染控制
    在这里插入图片描述

基于ArkTS声明式开发范式

  • UI开发(ArkTS声明式开发范式)概述
  • 开发布局
  • 添加组件
  • 显示图片
  • 使用动画
  • 支持交互事件
  • 性能提升的推荐方法

在这里插入图片描述

兼容JS的类Web开发范式

  • 概述
  • 框架说明
  • 构建用户界面
  • 常见组件开发指导
  • 动效开发指导
  • 自定义组件
    在这里插入图片描述

Web组件

  • 概述
  • 设置基本属性和事件
  • 并发
  • 窗口管理
  • WebGL
  • 媒体
  • 安全
  • 网络与连接
  • 电话服务
  • 数据管理

  • 在这里插入图片描述

应用模型

  • 概述
  • Stage模型开发指导
  • FA模型开发指导
    在这里插入图片描述
2024完整鸿蒙学习资料领取方式:扫描下方二维码即可
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值