当前位置:   article > 正文

前端 Web 与原生应用端 WebView 通信交互 - HarmonyOS Next

前端 Web 与原生应用端 WebView 通信交互 - HarmonyOS Next

基于鸿蒙 HarmonyOS Next 与前端 Vue 通信交互相关小结;
DevEco Studio NEXT Developer Preview2
Vue js

两端相互拟定好协议后,通过前端页面的点击事件,将所需的数据传输给原生移动端组件方法中,处理后将消息回传至前端.

根据官方文档的案例尝试,但没成功 ... 后经过几经尝试后两端握手成功 ... (官方文档略显粗糙,好一番折腾)

一.应用端

基于 import web_webview from '@ohos.web.webview' 并初始化 Web 组件;
调用 javaScriptProxy 方法,定义配置协议、参数和方法相关,具体可参考如下 code 片段;
name: 交互协议
object: 定义交互对象
methodList: 交互对象中所涵盖的方法,支持多个,也可以通过一个对象方法再细化多个子方法
controller: 组件

  1. import web_webview from '@ohos.web.webview'
  2. @Entry
  3. @Component
  4. export struct HomePage {
  5. controller: web_webview.WebviewController = new web_webview.WebviewController()
  6. ports: web_webview.WebMessagePort[] = [];
  7. nativePort: web_webview.WebMessagePort | null = null;
  8. message: web_webview.WebMessageExt = new web_webview.WebMessageExt();
  9. // 声明注册对象
  10. @State WebCallAppMethod: WebCallAppClass = new WebCallAppClass()
  11. aboutToAppear(): void {
  12. try {
  13. // 启用网页调试功能
  14. web_webview.WebviewController.setWebDebuggingAccess(true);
  15. } catch (error) {
  16. let e: business_error.BusinessError = error as business_error.BusinessError;
  17. console.log(`Error Code: ${e.code}, Message: ${e.message}`);
  18. this.controller.refresh(); // 页面异常,刷新
  19. }
  20. }
  21. build() {
  22. Row() {
  23. Column({ space: 20 }) {
  24. Web({ src: 'http://192.168.12.108:8080', controller: this.controller })
  25. .width('100%')
  26. .height('100%')
  27. .backgroundColor(Color.White)
  28. .multiWindowAccess(true)
  29. .javaScriptAccess(true)
  30. .geolocationAccess(true)
  31. .imageAccess(true)
  32. .onlineImageAccess(true)
  33. .domStorageAccess(true)
  34. .fileAccess(true)
  35. .mediaPlayGestureAccess(true)
  36. .mixedMode(MixedMode.All)
  37. .layoutMode(WebLayoutMode.FIT_CONTENT) // 自适应布局
  38. .verticalScrollBarAccess(true)
  39. .horizontalScrollBarAccess(false)
  40. .cacheMode(CacheMode.Default)
  41. .zoomAccess(false)// 禁止手势缩放
  42. .onConsole((event) => {
  43. console.log('[交互] - onConsole')
  44. LogUtils.info(event?.message.getMessage())
  45. return false
  46. })
  47. .onPageBegin(() => { // 页面加载中
  48. // this.registerWebJavaScript()
  49. })
  50. .onPageEnd(() => { // 页面加载完成
  51. console.log('[Web] - 页面加载完成')
  52. // this.registerWebJavaScript()
  53. })
  54. .onErrorReceive((event) => { // 异常: 无网络,页面加载错误时
  55. if (event) {
  56. console.info('getErrorInfo:' + event.error.getErrorInfo());
  57. console.info('getErrorCode:' + event.error.getErrorCode());
  58. console.info('url:' + event.request.getRequestUrl());
  59. console.info('isMainFrame:' + event.request.isMainFrame());
  60. console.info('isRedirect:' + event.request.isRedirect());
  61. console.info('isRequestGesture:' + event.request.isRequestGesture());
  62. console.info('getRequestHeader_headerKey:' + event.request.getRequestHeader().toString());
  63. let result = event.request.getRequestHeader();
  64. console.info('The request header result size is ' + result.length);
  65. for (let i of result) {
  66. console.info('The request header key is : ' + i.headerKey + ', value is : ' + i.headerValue);
  67. }
  68. }
  69. })
  70. .onHttpErrorReceive((event) => { // 异常: 网页加载资源 Http code >= 400
  71. if (event) {
  72. console.info('url:' + event.request.getRequestUrl());
  73. console.info('isMainFrame:' + event.request.isMainFrame());
  74. console.info('isRedirect:' + event.request.isRedirect());
  75. console.info('isRequestGesture:' + event.request.isRequestGesture());
  76. console.info('getResponseData:' + event.response.getResponseData());
  77. console.info('getResponseEncoding:' + event.response.getResponseEncoding());
  78. console.info('getResponseMimeType:' + event.response.getResponseMimeType());
  79. console.info('getResponseCode:' + event.response.getResponseCode());
  80. console.info('getReasonMessage:' + event.response.getReasonMessage());
  81. let result = event.request.getRequestHeader();
  82. console.info('The request header result size is ' + result.length);
  83. for (let i of result) {
  84. console.info('The request header key is : ' + i.headerKey + ' , value is : ' + i.headerValue);
  85. }
  86. let resph = event.response.getResponseHeader();
  87. console.info('The response header result size is ' + resph.length);
  88. for (let i of resph) {
  89. console.info('The response header key is : ' + i.headerKey + ' , value is : ' + i.headerValue);
  90. }
  91. }
  92. })
  93. .onConfirm((event) => { // 提示框处理相关
  94. AlertDialog.show({
  95. title: '温馨提示',
  96. message: event?.message,
  97. confirm: {
  98. value: 'onAlert',
  99. action: () => {
  100. event?.result.handleConfirm()
  101. }
  102. },
  103. cancel: () => {
  104. event?.result.handleCancel()
  105. }
  106. })
  107. return true;
  108. })
  109. .onShowFileSelector((event) => { // 文件上传处理相关
  110. console.log('MyFileUploader onShowFileSelector invoked');
  111. const documentSelectOptions = new picker.PhotoSelectOptions();
  112. let uri: string | null = null;
  113. const documentViewPicker = new picker.PhotoViewPicker();
  114. documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => {
  115. uri = documentSelectResult[0];
  116. console.info('documentViewPicker.select to file succeed and uri is:' + uri);
  117. if (event) {
  118. event.result.handleFileList([uri]);
  119. }
  120. }).catch((err: BusinessError) => {
  121. console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
  122. })
  123. return true;
  124. })
  125. .javaScriptProxy({ // web call app
  126. // 对象注入 web
  127. object: this.WebCallAppMethod,
  128. name: 'WebCallApp', // AppCallWeb WebCallAppSsss WebCallApp
  129. methodList: ['WebCallApp', 'CmdTest', 'CmdOpenUrl'],
  130. controller: this.controller
  131. })
  132. }.width('100%').height('100%')
  133. }
  134. }
  135. }
  136. class WebCallAppClass {
  137. constructor() {
  138. }
  139. WebCallApp(value: string): string { // 采用该 Json 对象模式,通过解析对象中的 type 后细化对应不同的子方法
  140. console.log('[交互] --- WebCallApp - 测试')
  141. console.log(value)
  142. console.log(JSON.parse(value))
  143. return '[交互] --- WebCallApp - 测试 - 回调123123123'
  144. }
  145. CmdOpenUrl(): Object {
  146. console.log('[交互] --- WebCallApp - CmdOpenUrl');
  147. new Object({
  148. 'command': '111',
  149. })
  150. return Object;
  151. }
  152. CmdTest(value: string): string {
  153. console.log('[交互] --- WebCallApp - test');
  154. console.log(value);
  155. return '[交互] --- WebCallApp - test';
  156. }
  157. }

二.前端

前端配置有两种方式,可以通过 index.html 配置 js 后调用,也可以单独另起一个 js 类方法去适配,具体可参考如下 code 按需尝试;

方式一.通过 index.html

在项目根目录的 index.html 文件中添加如下 script 段落后,在业务所需的地方调用即可

  1. // index.html
  2. <script>
  3. // eruda.init()
  4. (function () {
  5. if (!window.applicationCache && typeof(Worker)=='undefined') {
  6. alert("E001-检测到您的环境不支持HTML5,程序终止运行!");//不支持HTML5
  7. return;
  8. }
  9. var global = window; // create a pointer to the window root
  10. if (typeof WebCallAppSsss === 'undefined') {
  11. global.WebCallApp = { // 此处的 WebCallApp 与原生端 javaScriptProxy 中的 name 相互匹配
  12. // 如下方法对应的是 javaScriptProxy 中 object 对象中的方法
  13. WebCallApp: (arg) => {},
  14. CmdOpenUrl: (arg) => {},
  15. CmdTest: (arg) => {},
  16. }; // create elf root if not existed
  17. }
  18. window.WebCallAppSsss.global = global; // add a pointer to window
  19. window.AMap.global = global;
  20. })();
  21. </script>
  1. // 业务所需的点击事件方法中调用
  2. methods : {
  3. onClickGoBack() {
  4. let str = WebCallAppSsss.CmdTest('[交互] - 测试'); // 方式一
  5. // this.webApp.WebCallApp('CmdTest', '111111'); // 方式二
  6. Toast.success('abc');
  7. },
  8. }

方式二.通过自定义 js

自定义 webCallApp 类,通过引入类方法调用

  1. // WebCallApp.js
  2. import {
  3. AppCallBacks,
  4. AppCommendBackHandlers,
  5. } from './AppMsgHandlers'
  6. export default {
  7. WebCallApp(command,args,callback,context) {
  8. /**
  9. * 交互
  10. *
  11. * 协议:command
  12. * 入参:args
  13. * 回调:callback
  14. * 参数:context
  15. * */
  16. if (typeof Elf.AppCallWeb != "undefined") {
  17. if (this.isInHarmonyOS()) { // 鸿蒙 HarmonyOS Next
  18. console.log('[OpenHarmony] - ' + command);
  19. let params = {
  20. args: args, // 入参
  21. command: command // 交互协议
  22. };
  23. console.log('[鸿蒙] - 入参');
  24. console.log(params);
  25. if (typeof WebCallApp === 'undefined') {
  26. Elf.WebCallApp = {
  27. WebCallApp: (args) => {}
  28. };
  29. }
  30. window.WebCallApp.Elf = Elf;
  31. } else { // Android & iOS
  32. context = context || window;//默认为window对象
  33. args = args || {};
  34. let sn = null;
  35. //IOS调用相机--sn特殊处理
  36. if (command == "callCamera") {
  37. sn = "examCamera";
  38. } else {
  39. sn = this.getSerialNumber();//请求App统一加水单号
  40. }
  41. let params = {
  42. args: args,
  43. command: command
  44. };
  45. //绑定回调函数
  46. if (callback) {
  47. AppCallBacks[sn] = {
  48. callback: callback,
  49. context: context
  50. };
  51. }
  52. if (window.webkit && window.webkit.messageHandlers) {
  53. //IOS
  54. params.sn = sn;
  55. window.webkit.messageHandlers["WebCallApp"].postMessage(JSON.stringify(params));
  56. } else if (Elf.WebCallApp) {
  57. //Android
  58. params.sn = sn;
  59. Elf.WebCallApp(JSON.stringify(params));
  60. } else {
  61. }
  62. }
  63. }
  64. },
  65. isInApp() {
  66. if (typeof Elf.AppCallWeb != "undefined") {
  67. return !!((window.webkit && window.webkit.messageHandlers) || typeof Elf.WebCallApp == "function" || typeof Elf.WebCallCef == "function");
  68. }
  69. },
  70. isInIOS() {
  71. return window.webkit && window.webkit.messageHandlers;
  72. },
  73. isInAndroid() {
  74. if (typeof Elf.AppCallWeb != "undefined") {
  75. return typeof Elf.WebCallApp == "function";
  76. }
  77. },
  78. isInHarmonyOS() {
  79. if (navigator.userAgent.toLowerCase().indexOf('openharmony') !== -1) {
  80. return true;
  81. } else {
  82. return false;
  83. }
  84. },
  85. getSerialNumber() {
  86. var uuid = this.UUID(3,8);
  87. return new Date().format("yyyyMMddhhmmssS") + uuid;
  88. },
  89. UUID(len,radix) {
  90. var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
  91. var uuid = [],
  92. i;
  93. radix = radix || chars.length;
  94. if (len) {
  95. for (i = 0; i < len; i++) {
  96. uuid[i] = chars[0 | Math.random() * radix];
  97. }
  98. } else {
  99. var r;
  100. uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
  101. uuid[14] = '4';
  102. for (i = 0; i < 36; i++) {
  103. if (!uuid[i]) {
  104. r = 0 | Math.random() * 16;
  105. uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
  106. }
  107. }
  108. }
  109. return uuid.join('');
  110. },
  111. }
  1. /* eslint-disable */
  2. // AppMsgHandlers
  3. import webApp from './index';
  4. /*
  5. *
  6. * app对接
  7. * 移动端种植Elf对象
  8. * window => Elf
  9. *
  10. */
  11. (function () {
  12. if (!window.applicationCache&&typeof(Worker)=='undefined') {
  13. alert("E001-检测到您的环境不支持HTML5,程序终止运行!");//不支持HTML5
  14. return;
  15. }
  16. var global = window;//create a pointer to the window root
  17. if (typeof Elf === 'undefined') {
  18. global.Elf = {};//create elf root if not existed
  19. }
  20. Elf.global = global;//add a pointer to window
  21. })();
  22. var AppCallBacks = {},//动态数据流水列表
  23. AppCommendBackHandlers = [],//APP后退监听事件列表
  24. APPCommendBookStateHandlers = [],//下载状态监听事件列表
  25. AppCommendRefreshHandlers = [],//刷新监听事件列表
  26. APPCommendAddToBookShelfHandlers = [],//添加到书架监听事件列表
  27. APPCommendAddToObtainedBookHandlers = [],//添加到已获得图书列表监听
  28. APPCommendReBackHandlers = [],//监听重新回到页面通知
  29. AppCommendNetworkHandlers = [],//监听网络链接状态
  30. AppCommendAppStartingHandlers = [],//监听APP进入后台运行
  31. AppCommendAppReactivateHandlers = [],//监听APP重新进入前台运行
  32. AppCommendScreenShotss = [],//监听手机截屏
  33. AppCommendKeyboardBounceUp = [];//监听移动端软键盘事件
  34. if (typeof Elf != "undefined") {
  35. Elf.AppCallWeb = (sn,result) => {
  36. if (result && typeof result == "string") {
  37. result = decodeURIComponent(result.replace(/\+/g,'%20'));
  38. try {
  39. result = JSON.parse(result);//解决空格变成+的问题
  40. } catch (error) {
  41. AppCallBacks[sn].callback.call(AppCallBacks[sn].context,result);
  42. return;
  43. }
  44. if (result.sn) {
  45. AppCallBacks[sn].callback.call(AppCallBacks[sn].context,result.QrCode);
  46. return;
  47. }
  48. }
  49. if (AppCallBacks[sn]) {
  50. if (JSON.parse(result.opFlag)) {
  51. //执行对应回调
  52. AppCallBacks[sn].callback.call(AppCallBacks[sn].context,(typeof result.serviceResult == "string") ? JSON.parse(result.serviceResult) : result.serviceResult);
  53. } else {
  54. //接口调用返回失败信息,统一处理错误消息
  55. Toast(result.errorMessage ? result.errorMessage : "服务器异常!");
  56. }
  57. //调用完成删除对象
  58. delete AppCallBacks[sn];
  59. } else if (AppMsgHandlers[sn] && typeof AppMsgHandlers[sn] == "function") {
  60. //处理消息通知
  61. AppMsgHandlers[sn].call(window,result);
  62. }
  63. };
  64. }
  65. export {
  66. AppCallBacks,
  67. AppCommendBackHandlers
  68. }

业务方法调用

  1. // 业务所需的点击事件方法中调用
  2. methods : {
  3. onClickGoBack() {
  4. // let str = WebCallAppSsss.CmdTest('[交互] - 测试'); // 方式一
  5. this.webApp.WebCallApp('CmdTest', '111111'); // 方式二
  6. Toast.success('abc');
  7. },
  8. }

以上便是此次分享的全部内容,希望能对大家有所帮助!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/680120
推荐阅读
相关标签
  

闽ICP备14008679号