Electron 移植到鸿蒙平台的架构实现:一次跨平台的技术探索

项目概述

本文将深入剖析一个将 Electron 框架移植到鸿蒙操作系统的项目实现。这个项目的核心目标是让基于 Electron 开发的桌面应用能够在鸿蒙生态系统中运行,为开发者提供一条快速将现有 Electron 应用迁移到鸿蒙平台的路径。

技术背景

Electron 是一个使用 JavaScript、HTML 和 CSS 构建跨平台桌面应用的框架,广泛应用于 VSCode、Slack 等知名应用。而鸿蒙作为华为推出的新一代操作系统,拥有独特的分布式架构和 ArkTS/ETS 开发语言体系。将 Electron 移植到鸿蒙平台,需要解决两个生态系统之间的巨大差异。

项目架构设计

1. 双模块架构

项目采用了清晰的双模块架构:

ohos_hap/
├── electron/              # Electron 应用主模块
│   ├── libs/             # 原生 C++ 库
│   │   ├── libelectron.so    # Electron 核心库
│   │   ├── libadapter.so     # 适配器桥接库
│   │   ├── libffmpeg.so      # 多媒体支持
│   │   └── libc++_shared.so
│   └── src/main/ets/     # ArkTS 代码
│       ├── entryability/ # 各种 Ability 入口
│       └── pages/        # UI 页面
│
└── web_engine/           # Web 引擎适配层(HAR 库)
    └── src/main/ets/
        ├── adapter/      # 40+ 系统适配器
        ├── jsbindings/   # JS 绑定层
        ├── components/   # UI 组件
        └── ability/      # 基础 Ability 类
Electron 模块
  • 作用: 应用的入口模块,负责启动和管理 Electron 应用实例
  • 核心组件:
    • BrowserAbility: 浏览器窗口能力
    • EntryAbility: 主入口能力
    • StatelessAbility: 无状态能力
    • 原生动态库(.so 文件)
Web Engine 模块
  • 作用: 可复用的 HAR 库,封装了 Electron 运行所需的所有适配逻辑
  • 设计理念: 通过适配器模式将鸿蒙 API 映射到 Electron API

2. 适配器模式:系统能力的桥接

项目的核心创新在于使用了适配器模式来桥接 Electron API 和鸿蒙系统能力。在 web_engine/src/main/ets/adapter/ 目录下,实现了 40+ 个适配器类,覆盖了 Electron 应用所需的各种系统能力:

核心适配器类别
// 1. 窗口管理适配器
AppWindowAdapter        // 应用窗口管理
SubWindowAdapter        // 子窗口管理
PopupWindowAdapter      // 弹窗管理
SystemFloatingWindowAdapter  // 系统悬浮窗

// 2. 输入输出适配器
MultiInputAdapter       // 多点触控和手势
DragDropAdapter         // 拖拽功能
IMFAdapter              // 输入法框架
PasteBoardAdapter       // 剪贴板

// 3. 设备能力适配器
CameraAdapter           // 相机
BluetoothAdapter        // 蓝牙
DeviceAdapter           // USB 设备
MediaAdapter            // 媒体播放

// 4. 系统集成适配器
NotificationAdapter     // 系统通知
PermissionManagerAdapter // 权限管理
FileManagerAdapter      // 文件管理
PrintAdapter            // 打印功能

// 5. UI 与显示适配器
DisplayAdapter          // 显示屏幕信息
NativeThemeAdapter      // 系统主题(深色/浅色模式)
CursorAdapter           // 鼠标光标
FontAdapter             // 字体管理
适配器实现示例

PermissionManagerAdapter 为例,展示了如何将鸿蒙的权限系统映射到 Electron:

@injectable()
export class PermissionManagerAdapter extends BaseAdapter {
  private readonly needPermissions: Map<string, Array<Permissions>> = new Map([
    ['location', ['ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION']],
    ['microphone', ['ohos.permission.MICROPHONE']],
    ['camera', ['ohos.permission.CAMERA']],
    // ... 更多权限映射
  ]);

  requestPermissions(permissionType: string, callback: (granted: number) => void) {
    // 将 Electron 风格的权限请求转换为鸿蒙 API 调用
  }
}

3. 依赖注入架构

项目使用 InversifyJS 实现了完整的依赖注入(DI)系统,这是一个企业级的设计模式:

// AdapterModule.ets - 注册所有适配器
export const adapterModule = new ContainerModule((bind) => {
  bind(AppLifecycleAdapter).toSelf();
  bind(ContextAdapter).toSelf();
  bind(DisplayAdapter).toSelf();
  bind(DragDropAdapter).toSelf();
  // ... 40+ 适配器注册
});

// 使用时通过 Inject 获取
private displayAdapter: DisplayAdapter = Inject.get(DisplayAdapter);

优势

  • 松耦合:模块之间通过接口交互
  • 易测试:可以轻松注入 Mock 对象
  • 可维护:依赖关系清晰可控

核心技术实现

1. 原生桥接层:libadapter.so

这是整个架构中最关键的一环,负责 ArkTS/ETS 代码与 C++ Electron 引擎之间的通信。

JS Binding 机制
// JsBindingUtils.ets
import adapter from 'libadapter.so';

export default class JsBindingUtils {
  static getNativeContext(contextType: number): NativeContext {
    return adapter.getNativeContext(contextType);
  }

  static bindFunction(name: string, func: Function) {
    this.currentContext.JSBind.bindFunction(name, func);
  }
}
双向调用流程
ArkTS Layer (ETS)
    ↕ [JSBind]
Native Bridge (libadapter.so)
    ↕ [C++ Binding]
Electron Engine (libelectron.so)

工作原理

  1. ETS → C++: 通过 adapter.getNativeContext() 获取原生上下文对象,调用 C++ 方法
  2. C++ → ETS: 通过 JSBind.bindFunction() 将 ETS 函数注册给 C++ 层回调

2. XComponent 渲染管道

Electron 的渲染输出通过鸿蒙的 XComponent 组件实现:

// WebWindow.ets
class WindowNodeController extends NodeController {
  makeNode(uiContext: UIContext): FrameNode | null {
    this.xComponent = TypedNode.createNode(uiContext, "XComponent", {
      type: XComponentType.SURFACE,
      controller: this.xComponentController
    });
    
    this.xComponent.initialize({
      id: this.config.moduleName,
      type: XComponentType.SURFACE,
      libraryname: "adapter"  // 关联 libadapter.so
    });
    
    this.xComponent.attribute
      .onLoad(() => {
        // 初始化窗口边界
        this.setDefaultBounds();
        // 启动 Electron 浏览器
        let vec_args = this.buildArgs();
        this.config.nativeContext.runBrowser(vec_args);
      })
  }
}

关键特性

  • XComponentType.SURFACE: 直接渲染原生内容
  • libraryname: "adapter": 加载 libadapter.so 进行原生渲染
  • 支持手势识别(Pan、Pinch、Tap)和拖拽事件

3. 多进程架构

项目实现了类似 Electron 的多进程模型:

// module.json5
{
  "abilities": [
    {
      "name": "EntryAbility",
      "launchType": "specified"  // 主进程
    },
    {
      "name": "BrowserAbility",
      "process": ":browser"      // 浏览器进程
    },
    {
      "name": "StatelessAbility",
      "process": ":browser"      // 渲染进程
    }
  ]
}

进程隔离优势

  • 主进程崩溃不影响浏览器窗口
  • 独立的内存空间提高稳定性
  • 符合 Electron 原有的进程模型

4. 窗口生命周期管理

WebAbility 类实现了完整的窗口生命周期管理:

export class WebAbility extends WebBaseAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    super.onCreate(want, launchParam);
    // 初始化 KV 存储、更新 WebApp 图标
    kvManager.init(this.context);
    this.updateWebAppIcon();
    
    // 注入各种适配器
    this.displayAdapter = Inject.get(DisplayAdapter);
    this.nativeThemeAdapter = Inject.get(NativeThemeAdapter);
    Inject.get(PermissionManagerAdapter).initPermissions();
  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    // 监听窗口事件
    windowStage.getMainWindow((err, data) => {
      data.on('windowStatusChange', (status) => {
        this.nativeContext.OnWindowStatusChange(this.xcomponentId, status);
      });
      
      data.on('windowSizeChange', (size) => {
        let windowBound = this.calculateBounds(data, size);
        this.nativeContext.OnWindowSizeChange(this.xcomponentId, windowBound);
      });
      
      // ... 更多事件监听
    });
    
    // 加载 UI 内容
    windowStage.loadContent(this.getContentPath(), storage);
  }

  async onPrepareToTerminateAsync(): Promise<boolean> {
    // 优雅关闭:等待浏览器响应
    this.nativeContext.OnWindowEvent(this.xcomponentId, WebAbility.WINDOW_CLOSE);
    for (let i = 0; i < MAXIMUM_NUM_OF_SLEEP_CYCLES; i++) {
      await this.sleep(SLEEP_INTERVAL_MS);
      let response = this.nativeContext.GetBrowserCloseResponse(id);
      if (response !== BrowserCloseResponse.kUndetermined) {
        break;
      }
    }
    return response === BrowserCloseResponse.kClosingContinue;
  }
}

生命周期特点

  • 完整的创建、激活、后台、销毁流程
  • 支持异步关闭确认(处理未保存数据)
  • 窗口事件实时同步到 Electron 层

技术亮点与创新

1. 适配器自动绑定机制

通过 JsBindingMethod 类统一管理所有适配器的绑定:

export class JsBindingMethod {
  static bind() {
    AccessibilityAdapterBind.bind();
    AppLifecycleAdapterBind.bind();
    BluetoothAdapterBind.bind();
    // ... 40+ 适配器自动绑定
    WebAppAdapterBind.bind();
  }
}

每个 AdapterBind 类将鸿蒙 API 函数注册到 C++ 层,实现透明调用。

2. 深色模式与主题适配

完整支持鸿蒙的深色模式和系统主题切换:

onConfigurationUpdate(config: Configuration) {
  if (config.colorMode !== undefined) {
    this.nativeThemeAdapter?.setSystemNativeTheme(config.colorMode);
  }
  if (config.fontSizeScale !== undefined) {
    this.displayAdapter?.onSystemFontSizeChange(config.fontSizeScale);
  }
}

3. 设备模式自适应

支持鸿蒙的 2in1 设备(平板电脑模式)和普通平板模式:

private onPadWindowStageCreate(windowClass: window.Window, storage: LocalStorage) {
  // 设置避让区域(状态栏、导航栏)
  this.updateAvoidAreaStatus(false);
  
  // 注册设备模式切换监听
  this.deviceInfoAdapter?.registerDeviceModeChange(context);
  
  windowClass.on('avoidAreaChange', (data) => {
    // 动态调整窗口布局
    let statusBarHeight = data.area.topRect.height;
    storage.setOrCreate('statusBarHeight', statusBarHeight);
  });
}

4. 拖拽与手势支持

实现了完整的拖拽功能,包括从外部应用拖入文件:

.onDragEnter((event?: DragEvent) => {
  this.dragDropAdapter.dragEnterData(this.xComponentId, event.getSummary());
})
.onDragMove((event?: DragEvent) => {
  let windowX = vp2px(event?.getWindowX());
  let windowY = vp2px(event?.getWindowY());
  this.dragDropAdapter.dragMove(this.xComponentId, windowX, windowY);
})
.onDrop((event?: DragEvent) => {
  let dragData: UnifiedData = event.getData();
  this.dragDropAdapter.dropData(this.xComponentId, dragData);
})

5. 权限管理透明化

将 Electron 的权限模型映射到鸿蒙:

private readonly needPermissions: Map<string, Array<Permissions>> = new Map([
  ['location', ['ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION']],
  ['microphone', ['ohos.permission.MICROPHONE']],
  ['camera', ['ohos.permission.CAMERA']],
  ['bluetooth', ['ohos.permission.ACCESS_BLUETOOTH']],
  ['screen_capture', ['ohos.permission.CUSTOM_SCREEN_CAPTURE']]
]);

requestPermissions(permissionType: string, callback: (granted: number) => void) {
  let permissions = this.needPermissions.get(permissionType);
  // 调用鸿蒙权限请求 API
  this.atManager.requestPermissionsFromUser(this.context, permissions)
    .then((data) => {
      callback(data.authResults[0]);
    });
}

技术挑战与解决方案

挑战 1: 渲染差异

问题: Electron 使用 Chromium 渲染,鸿蒙需要通过 XComponent 接入原生渲染。

解决:

  • 使用 XComponentType.SURFACE 直接渲染原生内容
  • 通过 libadapter.so 将 Chromium 的渲染输出映射到 XComponent Surface

挑战 2: API 差异

问题: Electron Node.js API 与鸿蒙 ArkTS API 体系完全不同。

解决:

  • 适配器模式:每个 Electron API 对应一个鸿蒙适配器
  • 统一接口:所有适配器继承 BaseAdapter
  • 依赖注入:通过 InversifyJS 管理依赖关系

挑战 3: 多进程通信

问题: Electron 的 IPC 机制需要在鸿蒙上重新实现。

解决:

  • 利用鸿蒙的 process 属性实现进程隔离
  • 通过 AbilityManager 管理多个 Ability 实例
  • 使用 KV 存储(kvManager)实现进程间数据共享

挑战 4: 窗口管理复杂性

问题: 鸿蒙的窗口系统与桌面操作系统差异很大。

解决:

  • 实现 WindowStyle 管理类统一窗口样式
  • 监听所有窗口事件并同步到 Electron 层
  • 适配平板的自由窗口模式和普通模式

构建与部署

依赖库

// web_engine/oh-package.json5
{
  "dependencies": {
    "inversify": "^6.0.1",        // 依赖注入框架
    "reflect-metadata": "^0.1.13" // 元数据反射
  },
  "devDependencies": {
    'libadapter.so': 'file:./src/main/cpp/types/libadapter'
  }
}

原生库

项目包含多个关键的 C++ 动态库:

  • libelectron.so: Electron 引擎核心(约 200MB+)
  • libadapter.so: 适配器桥接层
  • libffmpeg.so: 多媒体编解码支持
  • libc++_shared.so: C++ 标准库

构建配置

// build-profile.json5
{
  "app": {
    "products": [{
      "name": "default",
      "compatibleSdkVersion": "6.0.0(20)",  // HarmonyOS NEXT
      "runtimeOS": "HarmonyOS",
      "buildOption": {
        "nativeLib": {
          "collectAllLibs": true  // 自动收集所有 .so 文件
        }
      }
    }]
  }
}

性能优化

1. 懒加载策略

使用 ArkTS 的 lazy 关键字延迟加载非关键模块:

import lazy { DragDropAdapter } from '../adapter/DragDropAdapter';
import lazy { DeviceInfoAdapter } from '../adapter/DeviceInfoAdapter';

2. 内存管理

  • 及时释放 Ability 资源
  • 使用 aboutToDisappear 清理事件监听
  • 分进程隔离内存空间

3. 渲染优化

  • 使用 NodeController 代替传统组件渲染
  • RenderFit.TOP_LEFT 避免不必要的布局计算
  • 背景色预设减少闪烁

应用场景

这个项目适用于以下场景:

  1. 现有 Electron 应用迁移: 快速将桌面应用迁移到鸿蒙平台
  2. 跨平台开发: 使用相同的代码库支持 Windows、macOS、Linux 和 HarmonyOS
  3. 企业应用: 利用 Electron 生态的丰富库和工具链
  4. Web 技术栈开发者: 使用熟悉的 HTML/CSS/JS 开发鸿蒙应用

未来展望

可能的改进方向

  1. 性能优化: 进一步减少渲染延迟和内存占用
  2. API 完善: 补充更多 Electron API 的适配实现
  3. 开发工具: 提供调试工具和性能分析工具
  4. 文档完善: 提供详细的迁移指南和最佳实践

生态建设

  • 建立社区支持 Electron on HarmonyOS
  • 提供示例应用和模板项目
  • 集成鸿蒙特有能力(如分布式任务)

总结

将 Electron 移植到鸿蒙平台是一个极具挑战性的工程,涉及跨平台、跨语言、跨架构的多重技术难题。本项目通过以下核心技术实现了这一目标:

  1. 适配器模式: 40+ 系统适配器桥接两个生态
  2. 原生桥接: libadapter.so 实现 ArkTS 与 C++ 的双向调用
  3. 依赖注入: InversifyJS 构建松耦合的模块系统
  4. XComponent 渲染: 原生渲染管道支持 Chromium 内容
  5. 多进程架构: 保持 Electron 的进程隔离模型

这个项目不仅是技术移植,更是对跨平台开发理念的一次深入实践。它证明了通过合理的架构设计和技术选型,可以在保持应用原有特性的同时,让其在全新的平台上焕发生机。

对于想要进入鸿蒙生态的开发者,这个项目提供了一个极好的参考案例,展示了如何系统性地解决平台差异问题,实现真正的跨平台兼容。


项目关键指标:

  • 🏗️ 架构模块: 2 个(electron + web_engine)
  • 🔌 适配器数量: 40+ 个系统适配器
  • 📦 原生库: 4 个 .so 文件
  • 🎯 支持设备: 平板和 2in1 设备
  • 💡 核心技术: ArkTS + C++ + Electron

本文基于实际项目代码分析撰写,涵盖架构设计、核心技术实现和工程实践经验。希望对从事跨平台开发和鸿蒙生态建设的开发者有所启发。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐