HarmonyOS Cordova 混合应用架构设计-Cordova 与 OpenHarmony 混合开发实战

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

📌 概述

在 HarmonyOS Next 生态中,Cordova 框架提供了一种优雅的方式来构建跨平台应用。本文将深入探讨如何设计和实现一个完整的 HarmonyOS Cordova 混合应用架构,包括从原生层到 Web 层的完整链路。

🔗 完整的链路流程

1. 应用启动流程

当用户启动应用时,HarmonyOS 系统会加载应用进程,执行 EntryAbility 的 onCreate 生命周期,然后加载 Index.ets 页面。Index.ets 初始化 Cordova 的 MainPage 组件,该组件负责创建 ArkWeb 容器并加载 rawfile/www/index.html。HTML 文件加载完成后,JavaScript 模块依次初始化数据库、业务逻辑和 UI 管理器,最后应用就绪并显示主界面。

2. 原生与 Web 通信流程

Web 层通过 cordova.exec() 调用原生功能。该方法接收成功回调、错误回调、插件名称、方法名称和参数数组。Cordova 框架将请求通过通信桥接转发给原生层,原生层执行相应功能后,通过 PluginResult 将结果返回给 JavaScript 的回调函数。这种机制实现了 Web 和原生的无缝通信。

📂 项目目录结构

项目采用模块化结构,分为 AppScope(应用全局配置)、entry(应用入口模块)和 cordova(Cordova 框架模块)三个主要部分。AppScope 包含应用配置和资源文件。entry 模块包含 EntryAbility.ets(应用入口)和 Index.ets(主页面)。cordova 模块包含 Cordova 框架代码和 Web 应用资源,Web 应用代码位于 rawfile/www 目录,包括 index.html、css 和 js 子目录。

🔧 核心组件详解

1. EntryAbility - 应用入口

EntryAbility 是 HarmonyOS 应用的入口点,负责应用的生命周期管理。

typescript 复制代码
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';

const TAG: string = 'EntryAbility';
const DOMAIN_NUMBER: number = 0xFF00;

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onCreate');
  }

  onDestroy(): void {
    hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onDestroy');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // 主窗口创建,加载 Index.ets 页面
    hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onWindowStageCreate');

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(DOMAIN_NUMBER, TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in loading the content.');
    });
  }

  onWindowStageDestroy(): void {
    // 主窗口销毁
    hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
    // 应用进入前台
    hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onForeground');
  }

  onBackground(): void {
    // 应用进入后台
    hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onBackground');
  }
}

代码解释:

EntryAbility 类继承自 UIAbility,是 HarmonyOS 应用的入口点。onCreate() 方法在应用创建时调用,用于初始化应用状态。onWindowStageCreate() 方法在窗口舞台创建时调用,这是加载主页面的关键时刻,通过 windowStage.loadContent() 方法加载 Index.ets 页面。loadContent() 的第一个参数是页面路径,第二个参数是回调函数,用于处理加载成功或失败的情况。onForeground() 在应用进入前台时调用,onBackground() 在应用进入后台时调用,这两个方法可以用于管理应用的资源和状态。

2. Index.ets - 主页面容器

Index.ets 是应用的主页面,负责初始化 Cordova 框架和加载 Web 应用。

typescript 复制代码
import {
  MainPage,
  pageBackPress,
  pageHideEvent,
  pageShowEvent,
  PluginEntry
} from '@magongshou/harmony-cordova/Index';

@Entry
@Component
struct Index {
  /**
   * Cordova 插件配置
   * 如果需要自定义原生插件,在这里配置
   */
  cordovaPlugs: Array<PluginEntry> = [];

  /**
   * 页面显示生命周期
   * 当页面从隐藏状态变为显示状态时调用
   */
  onPageShow() {
    pageShowEvent();
  }

  /**
   * 返回键拦截
   * 返回 true 表示拦截返回键,由 Cordova 处理
   */
  onBackPress() {
    pageBackPress();
    return true;
  }

  /**
   * 页面隐藏生命周期
   * 当页面从显示状态变为隐藏状态时调用
   */
  onPageHide() {
    pageHideEvent();
  }

  /**
   * 构建页面 UI
   * 使用 MainPage 组件加载 Cordova 应用
   */
  build() {
    RelativeContainer() {
      MainPage({
        isWebDebug: false,              // 是否开启 Web 调试
        cordovaPlugs: this.cordovaPlugs // 传入插件配置
      });
    }
    .height('100%')
    .width('100%')
  }
}

代码解释:

@Entry 和 @Component 是 ArkTS 的装饰器,标记这是应用的入口页面。Index 结构体定义了页面的逻辑和 UI。cordovaPlugs 是一个数组,用于配置自定义的原生插件。onPageShow() 方法在页面显示时调用,可以用于初始化页面状态。onBackPress() 方法拦截返回键事件,返回 true 表示由应用处理返回逻辑,而不是系统默认行为。onPageHide() 方法在页面隐藏时调用,可以用于清理资源。build() 方法定义了页面的 UI 结构,使用 RelativeContainer 作为根容器,然后在其中放置 MainPage 组件。MainPage 是由 Cordova 框架提供的组件,负责加载和管理 Web 应用。isWebDebug 参数控制是否开启 Web 调试模式,开发时可以设为 true 以便调试。

3. MainPage - Cordova 容器

MainPage 是由 Cordova 框架提供的组件,它负责初始化 ArkWeb 容器并加载 Web 应用。

typescript 复制代码
// MainPage 的初始化过程(框架内部实现)
// 1. 创建 ArkWeb 实例
// 2. 配置 ArkWeb 参数
// 3. 加载 rawfile/www/index.html
// 4. 初始化 Cordova 运行时
// 5. 注册插件通信桥接

MainPage 会自动:

  • 加载 rawfile/www/index.html 作为应用主页面
  • 初始化 Cordova 框架和运行时
  • 建立 Web 和原生之间的通信桥接
  • 处理页面生命周期事件

4. index.html - Web 应用入口

index.html 是 Web 应用的入口文件,包含了应用的 HTML 结构、样式和脚本引用。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: 'unsafe-eval' 'unsafe-inline'; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
    <title>待办事项</title>
    
    <!-- Cordova 框架脚本 -->
    <script src="cordova.js"></script>
    
    <!-- 应用样式 -->
    <link rel="stylesheet" href="css/ui-components.css">
    <link rel="stylesheet" href="css/app.css">
</head>
<body>
    <!-- 应用主容器 -->
    <div id="app-container">
        <!-- 侧边栏和主内容区 -->
    </div>

    <!-- 应用脚本 -->
    <script src="js/ui-components.js"></script>
    <script src="js/db.js"></script>
    <script src="js/taskManager.js"></script>
    <script src="js/categoryManager.js"></script>
    <script src="js/uiManager.js"></script>
    <script src="js/modules.js"></script>
    <script src="js/app.js"></script>
</body>
</html>

代码解释:

index.html 是 Web 应用的入口文件,定义了应用的 HTML 结构。<script src="cordova.js"></script> 是必须的,它加载 Cordova 框架脚本,建立 Web 和原生层的通信桥接。Content-Security-Policy 元标签定义了内容安全策略,允许加载本地资源和执行脚本。样式和脚本的加载顺序很重要,应该先加载基础库(如 ui-components.js 和 db.js),再加载业务逻辑(如 app.js),这样可以确保依赖关系正确。app-container 是应用的主容器,所有页面内容都会被渲染到这个 div 中。

🔄 应用初始化流程

当应用启动时,以下步骤会依次执行:

javascript 复制代码
// 1. Cordova 框架初始化
document.addEventListener('deviceready', function() {
    console.log('Cordova 已就绪');
    
    // 2. 初始化数据库
    db.init().then(() => {
        console.log('数据库初始化完成');
        
        // 3. 初始化业务模块
        taskManager.init();
        categoryManager.init();
        
        // 4. 初始化 UI
        uiManager.init();
        
        // 5. 加载初始数据
        uiManager.renderTaskList();
        
        console.log('应用初始化完成');
    });
});

流程说明:

应用初始化过程从 deviceready 事件开始,这个事件表示 Cordova 框架已完全加载,Web 和原生层的通信桥接已建立。在这个事件的回调函数中,首先调用 db.init() 初始化 IndexedDB 数据库,这是一个异步操作,返回一个 Promise。当数据库初始化完成后,依次初始化任务管理器(taskManager)和分类管理器(categoryManager),这些管理器负责业务逻辑。然后初始化 UI 管理器(uiManager),它负责页面的渲染和事件处理。最后调用 uiManager.renderTaskList() 加载初始数据并渲染到 UI,这样用户就可以看到应用的主界面。整个初始化过程是按顺序执行的,确保每个模块都在其依赖的模块初始化完成后才开始初始化。

🔌 插件通信机制

Web 调用原生功能

javascript 复制代码
// 使用 cordova.exec() 调用原生功能
cordova.exec(
    function(success) {
        // 成功回调
        console.log('原生功能执行成功:', success);
    },
    function(error) {
        // 错误回调
        console.error('原生功能执行失败:', error);
    },
    'PluginName',      // 插件名称
    'methodName',      // 方法名称
    [arg1, arg2]       // 参数数组
);

参数说明:

cordova.exec() 方法用于从 Web 层调用原生功能。第一个参数是成功回调函数,当原生方法执行成功时调用,接收原生方法返回的数据。第二个参数是错误回调函数,当原生方法执行失败时调用,接收错误信息。第三个参数是插件名称,必须与原生代码中注册的插件名称一致。第四个参数是要调用的方法名称。第五个参数是传递给原生方法的参数数组。这种机制实现了 Web 和原生的异步通信,Web 层不需要知道原生的具体实现细节,只需要调用 cordova.exec() 方法并提供回调函数来处理结果。

原生调用 Web 函数

在原生代码中,可以通过 Cordova 框架提供的接口调用 Web 层的函数。原生层可以在特定事件发生时(例如收到通知、传感器数据变化等)主动调用 Web 层的方法,将数据传递给 Web 应用。

typescript 复制代码
// ArkTS 代码示例 - 原生插件调用 Web 函数
import { CordovaPlugin, CallbackContext } from '@magongshou/harmony-cordova/Index';
import { PluginResult, MessageStatus } from '@magongshou/harmony-cordova/Index';

export class NotificationPlugin extends CordovaPlugin {
    // 当收到系统通知时,调用 Web 层的函数
    async onNotificationReceived(callbackContext: CallbackContext, args: string[]): Promise<void> {
        try {
            const notificationData = {
                title: args[0],
                message: args[1],
                timestamp: new Date().toISOString()
            };
            
            // 通过 Cordova 框架调用 Web 层的函数
            // 这会触发 Web 层注册的回调函数
            const result = PluginResult.createByString(
                MessageStatus.OK, 
                JSON.stringify(notificationData)
            );
            callbackContext.sendPluginResult(result);
        } catch (error) {
            const result = PluginResult.createByString(
                MessageStatus.ERROR, 
                (error as Error).message
            );
            callbackContext.sendPluginResult(result);
        }
    }
}

原生代码解释:

NotificationPlugin 是一个自定义的 Cordova 插件,继承自 CordovaPlugin。onNotificationReceived 方法在原生层收到系统通知时被调用。该方法接收 CallbackContext 对象和参数数组,CallbackContext 用于将结果返回给 Web 层。方法首先将通知数据封装成一个对象,包括标题、消息和时间戳。然后使用 PluginResult.createByString() 创建一个成功的结果对象,将数据序列化为 JSON 字符串。最后通过 callbackContext.sendPluginResult() 将结果发送回 Web 层,Web 层会收到这个数据并执行相应的回调函数。如果发生错误,会创建一个错误结果对象并发送给 Web 层。

Web 层接收原生数据

Web 层可以通过注册监听器来接收来自原生层的数据:

javascript 复制代码
// JavaScript 代码 - 监听原生层的通知
window.addEventListener('notification', function(event) {
    const notificationData = JSON.parse(event.detail);
    console.log('收到原生通知:', notificationData);
    
    // 显示通知给用户
    UIComponents.showToast(notificationData.message, {
        type: 'info',
        duration: 5000
    });
    
    // 更新应用状态
    if (notificationData.title === 'TaskReminder') {
        taskManager.handleReminder(notificationData);
    }
});

Web 层代码解释:

Web 层通过监听 'notification' 事件来接收来自原生层的数据。当原生层发送通知时,这个事件会被触发,event.detail 包含了原生层发送的数据。Web 层将 JSON 字符串解析为对象,然后根据通知的类型执行相应的操作。例如,如果通知是任务提醒,会调用 taskManager.handleReminder() 方法来处理这个提醒。这种机制实现了原生层主动向 Web 层推送数据的功能。

� 总结

HarmonyOS Cordova 混合应用架构通过清晰的分层设计,将应用分为 ArkTS 原生层、Cordova 框架层和 Web 应用层。这种架构既充分利用了 Web 技术的开发效率,又能够灵活地调用原生能力。通过 Cordova 的 exec 接口,Web 和原生层可以无缝通信,实现了高效的跨平台开发。

相关推荐
图形达人2 小时前
Siggraph Asia 2025,鸿蒙方舟图形引擎绽放,开创空间化体验元年!
华为·harmonyos
森之鸟2 小时前
uniapp——配置鸿蒙环境,进行真机调试
华为·uni-app·harmonyos
Next_Tech_AI2 小时前
别用 JS 惯坏了鸿蒙
开发语言·前端·javascript·个人开发·ai编程·harmonyos
梦想不只是梦与想2 小时前
鸿蒙中 Scroll可滚动组件
harmonyos·鸿蒙·scroll
码农小峰峰2 小时前
HarmonyOS应用上架全流程实战经验分享
华为·harmonyos
摘星编程4 小时前
React Native鸿蒙:Calendar日程标记显示
react native·react.js·harmonyos
mocoding4 小时前
使用鸿蒙化Flutter图片选择、相机拍照、多图选择三方库image_picker实战教程示例
flutter·前端框架·harmonyos·鸿蒙
酣大智4 小时前
DHCP中继配置实验
运维·网络·网络协议·tcp/ip·华为
一起养小猫5 小时前
Flutter for OpenHarmony 实战:电子英汉词典完整开发指南
flutter·harmonyos