全流程总览(Flowchart)

渲染错误: Mermaid 渲染失败: Parse error on line 6: ...rdova 解析 preferences(Hostname 等)] F - -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'

代码驱动解读(主线)

启动入口:EntryAbility 初始化 Web 引擎并加载页面

 // entry/src/main/ets/entryability/EntryAbility.ets
 import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { window } from '@kit.ArkUI';
 import { webview } from '@kit.ArkWeb';
 import { ConfigurationConstant } from '@kit.AbilityKit';
 
 export default class EntryAbility extends UIAbility {
   onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
     // ...(省略 ColorMode 设置与日志)
     webview.WebviewController.initializeWebEngine();
   }
 
   onWindowStageCreate(windowStage: window.WindowStage): void {
     windowStage.loadContent('pages/Index', (err) => {
       if (err.code) {
         // 错误处理 ...
         return;
       }
       // 加载成功日志 ...
     });
   }
 }

要点:

  • initializeWebEngine 在 Ability 创建时初始化 ArkWeb 内核。
  • loadContent(‘pages/Index’) 切入 UI 层 Index 页面。

代码说明:

  • EntryAbility 是应用的 UIAbility 入口,负责 ArkWeb 内核的一次性初始化与页面装载。
  • onCreate 中调用 initializeWebEngine(),确保后续 ArkUI Web 组件可用。
  • onWindowStageCreate 通过 windowStage.loadContent('pages/Index') 切换到 ArkUI 首屏页面;发生异常时提前返回避免空白页。

ArkUI 页面:Index 作为 MainPage 的父组件,负责生命周期透传

 // entry/src/main/ets/pages/Index.ets
 import {
   MainPage,
   pageBackPress,
   pageHideEvent,
   pageShowEvent,
   PluginEntry
 } from '@magongshou/harmony-cordova/Index';
 
 @Entry
 @Component
 struct Index {
   cordovaPlugs: Array<PluginEntry> = [];
 
   onPageShow() { pageShowEvent(); }
   onBackPress() { pageBackPress(); return true; }
   onPageHide() { pageHideEvent(); }
 
   build() {
     RelativeContainer() {
       MainPage({
         isWebDebug: false,
         cordovaPlugs: this.cordovaPlugs
       });
     }
     .height('100%')
     .width('100%')
   }
 }

要点:

  • 父组件把 页面生命周期返回键 事件传递给 Cordova
  • 渲染 MainPage,承载 WebView 与 Cordova 初始化。

代码说明:

  • Index 仅负责承载 MainPage 与透传生命周期,不直接操控 Web 内核。
  • onPageShow/onPageHide/onBackPress 分别映射为 Cordova 的 resume/pause/backbutton 事件。
  • return true 表示拦截系统返回键,由 Cordova(Web 层)决定导航与退出策略。
  • MainPage 参数中可注入 ArkTS 插件列表 cordovaPlugs,用于扩展原生能力。

MainPage:初始化 Cordova,计算默认入口并驱动 Web 载入

 // cordova/src/main/ets/components/MainPage.ets(片段)
 @Component
 export struct MainPage {
   private webTag: string = 'scheme-handler' + '-' + util.generateRandomUUID(false);
   private strTmpUrl:string = 'www.example.com';
   private src:string = 'https://' + this.strTmpUrl + '/index.html';
   // ... 省略属性
 
   aboutToAppear() {
     // ... 省略显示监听/Cookie 配置
     if (!cordovaWebIdToWebviewGlobe.hasKey(this.webTag)) {
       webview.WebviewController.setWebDebuggingAccess(this.isWebDebug);
       cordova.SetSchemeHandler(this.customSchemes, this.webTag);
       cordova.RegisterCustomSchemes(this.customSchemes);
       let preferences:string = cordova.InitCordova(
         this.webTag,
         this.strTmpUrl,
         PluginHandler,
         getContext(this).databaseDir,
         getContext(this).resourceManager,
         this.customHttpHeaders,
         this.isAllowCredentials
       );
       let jsonConfig:object = JSON.parse(preferences) as object;
       let arrayObject:Array<object> = jsonConfig?.['preferences'];
       arrayObject.forEach((obj:object)=>{
         if(obj?.['name'] && obj?.['value']) {
           if(obj?.['name'] === 'Hostname') {
             this.strTmpUrl = obj?.['value']; // 设置 Hostname
           }
           // ... 省略 Splash 参数解析
           this.cordovaPreferences.set(obj?.['name'], obj?.['value']);
         }
       })
 
       if (this.indexPage == '') { // 默认加载页面
         this.src = 'https://' + this.strTmpUrl + '/www/index.html';
       } else {
         // ... 省略自定义入口逻辑
       }
       InitArkTsPlugins(this.webId, this.webTag, this.cordovaPreferences, this.cordovaInterface, this.cordovaWebView, this.cordovaPlugs);
       // ... 省略生命周期设置与闪屏
     }
     this.cordovaWebView.setIsShow(true);
     // ... 通知插件、注册监听
   }
 
   @Builder
   builder() {
     Column() {
       Stack({ alignContent: Alignment.Top }) {
         Web({ src: this.src, controller: this.webviewController })
           .onControllerAttached(() => { /* 省略 */ })
           .onLoadIntercept((event) => { /* 省略 */ return false; })
           .onOverrideUrlLoading((req) => { return this.lifeCycle.execOnOverrideUrlLoading(req, this.parentPage); })
           .onInterceptRequest((event) => { /* 省略 */ return null; })
           .onPageBegin((event) => { /* 省略 */ })
           .onProgressChange((event) => { /* 省略 */ })
           .javaScriptAccess(true)
           // ... 省略若干能力开关
           .onPageEnd((event) => {
             this.title = this.webviewController.getTitle();
             this.cordovaWebView.setWebAttribute(this.webAttributeModifier.attribute);
             this.webviewController.setDownloadDelegate(this.downDelegate);
             if (this.onSetCordovaWebAttribute) {
               this.onSetCordovaWebAttribute(this.cordovaWebView);
             }
             if (event) {
               this.lifeCycle.execOnPageEnd(event.url, this.webviewController, this.parentPage);
             }
             this.splashScreenHide();
           })
           .onPrompt((event) => {
             // cordova 依赖此函数进行 JS 桥接(必须调用)
             let ret = cordova.onJsPrompt(this.webTag, event.url, event.message, event.value);
             if (ret !== undefined && ret != '') {
               event.result.handlePromptConfirm(ret);
               return false;
             } else if (ret === undefined) {
               // ... 省略弹窗回退逻辑
               return true;
             }
             return false;
           })
           // ... 省略地理位置、原生嵌入等
           .height('100%')
           .width('100%')
       }
       .height('100%')
       .width('100%')
     }
     .height('100%')
     .width('100%')
     .backgroundColor('#000000')
   }
 }

要点:

  • InitCordova 返回的 preferences 中可设定 Hostname,默认入口拼为 https://{Hostname}/www/index.html
  • SetSchemeHandler/RegisterCustomSchemes 配合 Cordova 原生资源路由,拦截并映射到 rawfile 资源包。
  • ArkUI Web 组件接收 src 并最终调用 loadUrl 加载页面,onPageEnd 隐藏闪屏。

代码说明:

  • aboutToAppear 的初始化保证每个 webTag 仅初始化一次,兼容多 WebView 场景。
  • InitCordova 读取 preferences 并设置 Hostname;若未指定 indexPage 则默认使用 /www/index.html
  • SetSchemeHandler/RegisterCustomSchemes 建立自定义协议到 rawfile 的映射,确保本地打包资源可被 Web 访问。
  • ArkUI Web 通过 Web({ src, controller }) 载入页面;onPrompt 驱动 Cordova JS 桥;onPageEnd 同步标题/下载代理/属性并隐藏闪屏。

JS 桥与页面事件:Index 透传 -> cordova.onArkTsResult

 // cordova/Index.ets(片段)
 import cordova from 'libcordova.so'
 import { ArkTsAttribute } from './src/main/ets/components/PluginGlobal';
 
 export function pageShowEvent() {
   let result: ArkTsAttribute = {content: 'resume', result: []};
   cordova.onArkTsResult(JSON.stringify(result), 'CoreHarmony', '');
 }
 
 export function pageHideEvent() {
   let result: ArkTsAttribute = {content: 'pendingPause', result: []};
   cordova.onArkTsResult(JSON.stringify(result), 'CoreHarmony', '');
 }
 
 export function pageBackPress() {
   let result: ArkTsAttribute = {content: 'overrideBackbutton', result: []};
   cordova.onArkTsResult(JSON.stringify(result), 'CoreHarmony', '');
 }

要点:

  • ArkTS 侧通过 onArkTsResult 将页面生命周期与返回键事件分发到 Cordova/JS 层,触发 backbuttonpause/resume 等回调。

代码说明:

  • 三个方法把 ArkTS 生命周期/按键事件封装为 ArkTsAttribute,经 cordova.onArkTsResult 发送到 JS 层。
  • JS 侧触发 Cordova 标准事件:pauseresumebackbutton,插件可据此处理状态与导航。
  • 保证 ArkTS 页面与 Web 应用生命周期一致,避免前后台状态错乱。

最终入口:rawfile/www/index.html(游戏首页)

 <!-- cordova/src/main/resources/rawfile/www/index.html(片段) -->
 <!DOCTYPE html>
 <html lang="zh-CN">
 <head>
   <meta charset="UTF-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>2048</title>
   <link rel="stylesheet" href="css/style.css" />
 </head>
 <body>
   <div class="wrap">
     <div class="head">
       <h1>2048</h1>
       <div class="stats">
         <div class="badge"><small>分数</small><b id="score">0</b></div>
         <div class="badge"><small>最佳</small><b id="best">0</b></div>
       </div>
     </div>
     <div class="toolbar">
       <button id="newGame">新游戏</button>
       <span class="tip">使用键盘方向键(↑↓←→)进行移动。</span>
     </div>
     <div class="board">
       <div id="grid" class="grid"></div>
     </div>
   </div>
   <script src="js/app.js"></script>
 </body>
 </html>

要点:

  • 资源路径以 www/ 为根,结合 Hostname 的自定义协议,由 Cordova 原生资源处理器映射到 rawfile

代码说明:

  • 页面结构包含标题、分数统计与棋盘容器 #gridjs/app.js 为游戏逻辑入口。
  • 静态资源路径相对 www/ 根,打包后通过原生资源处理器映射为 rawfile/www/...
  • 在鸿蒙 PC 端,键盘交互(方向键/WASD)由 Web 层处理,可与触控手势共存。

加载过程时序(Sequence Diagram)

Cordova WebView MainPage Index EntryAbility User Cordova WebView MainPage Index EntryAbility User 启动应用 initializeWebEngine() windowStage.loadContent('pages/Index') build() 渲染 InitCordova(webTag, Hostname, ...) preferences(JSON) loadUrl('https://{Hostname}/www/index.html') onPageEnd() splashScreenHide()

小结

  • 原生入口 EntryAbility 初始化 Web 引擎,加载 pages/Index
  • Index 作为父组件,渲染 MainPage 并上报生命周期/返回键至 Cordova。
  • MainPage 完成 Cordova 初始化,解析 Hostname,默认拼接 https://{Hostname}/www/index.html
  • ArkUI Web 组件加载 URL,onPageEnd 隐藏闪屏,最终渲染 index.html 游戏页面。

欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐