引言:Electron 与鸿蒙原子化服务的碰撞

在跨平台应用开发领域,Electron 凭借 “HTML+CSS+JavaScript” 的技术栈优势,成为桌面应用开发的热门选择(如 VS Code、Slack 均基于 Electron 构建)。而华为鸿蒙(HarmonyOS)作为面向全场景的分布式操作系统,其核心特性 “原子化服务” 打破了传统应用的安装壁垒,实现了 “即用即走、免安装” 的轻量化体验。

当 Electron 应用遇上鸿蒙原子化服务,开发者可以同时兼顾跨平台开发效率与鸿蒙生态的全场景能力 —— 既保留 Electron 对 Web 技术栈的兼容性,又能借助鸿蒙服务卡片、分布式流转等特性,让应用渗透到手机、平板、智慧屏等多终端场景。

本文将从 技术原理、环境搭建、轻量级打包、服务卡片集成、通信机制、实战案例、问题排查 七个维度,全面解析 Electron 应用适配鸿蒙原子化服务的完整流程,附带可直接复用的代码片段和官方文档链接,助力开发者快速落地适配工作。

本文适用于:Electron 开发者、鸿蒙生态开发者、跨平台应用工程师,需具备基础的 Node.js 开发经验和鸿蒙应用开发入门知识。参考文档:

一、核心概念解析:原子化服务与 Electron 适配逻辑

在开始实操前,需先明确两个核心技术的适配逻辑,避免踩坑:

1.1 鸿蒙原子化服务核心特性

原子化服务是鸿蒙生态的核心组件,本质是 免安装、轻量化、可被直接调用的应用形态,其核心特性包括:

  • 免安装启动:用户无需下载安装包,通过桌面卡片、搜索、分享等方式直接启动服务;
  • 服务卡片:支持在鸿蒙桌面展示核心功能(如天气、待办、数据统计),点击卡片可跳转至完整应用;
  • 分布式流转:服务可在多终端间无缝切换(如手机启动的 Electron 应用,流转至智慧屏继续使用);
  • 轻量化打包:以 HAP(HarmonyOS Ability Package)为载体,单个原子化服务包体积通常小于 10MB。

1.2 Electron 应用适配鸿蒙的核心挑战

Electron 应用的传统打包产物(如 .exe、.dmg)体积较大(通常 50MB+),且依赖系统原生环境,无法直接适配鸿蒙原子化服务的 “轻量化、免安装” 要求。因此,适配的核心逻辑是:

  1. 对 Electron 应用进行 瘦身优化,降低打包体积;
  2. 将优化后的 Electron 应用封装为鸿蒙 HAP 包,使其支持原子化服务启动;
  3. 开发鸿蒙 服务卡片,实现核心功能的桌面展示与应用跳转;
  4. 打通 Electron 应用与鸿蒙系统的 通信通道(如数据交互、生命周期管理)。

二、前置准备:开发环境搭建

2.1 鸿蒙开发环境配置

步骤 1:安装 DevEco Studio
步骤 2:配置鸿蒙开发者账号与签名
  • 注册鸿蒙开发者账号:华为开发者联盟
  • 申请应用签名:原子化服务打包需使用官方签名,参考 鸿蒙应用签名配置指南
  • 开通原子化服务权限:在华为开发者联盟后台,申请 “原子化服务发布” 权限(个人开发者需完成实名认证)

2.2 Electron 开发环境配置

步骤 1:安装 Node.js 与 Electron
  • Node.js 版本要求:16.x+(推荐 18.x LTS),下载地址:Node.js 官方下载
  • 初始化 Electron 项目:

    bash

    运行

    # 创建项目目录
    mkdir electron-harmony-atomic-service && cd electron-harmony-atomic-service
    # 初始化 npm 项目
    npm init -y
    # 安装 Electron(指定 25.x 版本,兼容性更优)
    npm install electron@25.9.0 --save-dev
    
步骤 2:安装核心依赖工具
  • electron-builder:Electron 打包工具,支持自定义打包配置;
  • electron-packager:轻量级打包工具,用于快速生成精简版应用;
  • @ohos/hap-toolkit:鸿蒙 HAP 包打包工具;
  • node-ipc:Electron 与鸿蒙系统的进程间通信工具。

安装命令:

bash

运行

npm install electron-builder electron-packager @ohos/hap-toolkit node-ipc --save-dev

2.3 环境验证

  • 验证鸿蒙环境:启动 DevEco Studio,创建 “原子化服务” 项目(模板选择 “Empty Ability”),能正常编译运行则环境配置成功;
  • 验证 Electron 环境:在项目根目录创建 main.js,写入以下代码:

    javascript

    运行

    const { app, BrowserWindow } = require('electron');
    function createWindow() {
      const win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: { nodeIntegration: true }
      });
      win.loadFile('index.html');
    }
    app.whenReady().then(createWindow);
    
    创建 index.html

    html

    预览

    <!DOCTYPE html>
    <html>
    <body>
      <h1>Electron 鸿蒙原子化服务适配Demo</h1>
    </body>
    </html>
    
    在 package.json 中添加启动脚本:

    json

    "scripts": {
      "start": "electron ."
    }
    
    运行 npm start,能正常启动 Electron 窗口则环境配置成功。

三、Electron 应用轻量级打包:体积优化与适配改造

Electron 应用的默认打包体积较大,需通过 “依赖裁剪、资源压缩、打包配置优化” 实现轻量化,使其满足鸿蒙原子化服务的体积要求(建议压缩至 10MB 以内)。

3.1 依赖裁剪:移除冗余依赖

步骤 1:分析依赖体积

使用 webpack-bundle-analyzer 分析项目依赖,找出冗余依赖:

bash

运行

npm install webpack-bundle-analyzer --save-dev

在项目根目录创建 webpack.config.js

javascript

运行

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
  plugins: [new BundleAnalyzerPlugin()]
};

运行 npx webpack --config webpack.config.js,浏览器会自动打开依赖分析页面,重点关注体积较大的非必要依赖。

步骤 2:替换重量级依赖
  • 用 axios 替换 superagent(体积减少 50%+);
  • 用 dayjs 替换 moment.js(体积减少 80%+);
  • 移除开发环境依赖(如 eslintmocha),确保仅保留生产环境依赖:

    bash

    运行

    # 卸载开发依赖
    npm uninstall eslint mocha --save-dev
    # 安装轻量级替代依赖
    npm install axios dayjs --save
    

3.2 资源压缩:优化静态资源

步骤 1:压缩 HTML/CSS/JS
  • 使用 html-minifier-terser 压缩 HTML:

    bash

    运行

    npm install html-minifier-terser --save-dev
    
    创建 compress-html.js

    javascript

    运行

    const htmlMinifier = require('html-minifier-terser');
    const fs = require('fs');
    const html = fs.readFileSync('index.html', 'utf8');
    const minifiedHtml = htmlMinifier.minify(html, {
      removeComments: true,
      collapseWhitespace: true,
      minifyCSS: true,
      minifyJS: true
    });
    fs.writeFileSync('dist/index.html', minifiedHtml);
    
  • 使用 csso 压缩 CSS,terser 压缩 JS,同理创建压缩脚本。
步骤 2:优化图片资源
  • 将图片格式转换为 WebP(体积减少 60%+),使用 Squoosh 在线压缩;
  • 移除图片中的 Exif 信息,使用 exiftool 工具:

    bash

    运行

    # 安装 exiftool(需先安装 Perl)
    npm install exiftool --save-dev
    

3.3 Electron 打包配置优化

使用 electron-packager 进行轻量级打包,配置如下:

步骤 1:修改 package.json 打包脚本

json

"scripts": {
  "start": "electron .",
  "package:light": "electron-packager . electron-harmony --platform=linux --arch=x64 --out=dist --overwrite --asar --prune=true --ignore=node_modules/electron --ignore=node_modules/.bin --ignore=tests"
}

参数说明:

  • --asar:将应用资源打包为 asar 归档文件,减少文件数量;
  • --prune=true:自动移除 node_modules 中的冗余文件;
  • --ignore:忽略不需要打包的文件(如测试目录、Electron 源码);
  • --platform=linux:鸿蒙系统基于 Linux 内核,需指定 Linux 平台。
步骤 2:运行打包命令

bash

运行

npm run package:light

打包完成后,在 dist/electron-harmony-linux-x64 目录下生成精简版 Electron 应用,体积可控制在 8-10MB(取决于应用复杂度)。

3.4 验证打包产物

运行打包后的应用,确保核心功能正常:

bash

运行

cd dist/electron-harmony-linux-x64
./electron-harmony

若能正常启动,说明轻量级打包成功。

进阶优化:使用 electron-builder 配置 extraResources 仅包含必要资源,或通过 electron-merge 合并冗余模块,进一步压缩体积。参考 Electron 体积优化官方指南

四、鸿蒙原子化服务适配:HAP 包封装与配置

将轻量化后的 Electron 应用封装为鸿蒙 HAP 包,使其支持原子化服务的启动、安装与分发。

4.1 创建鸿蒙原子化服务项目

  1. 打开 DevEco Studio,点击 “File → New → Project”;
  2. 选择 “HarmonyOS → Atomic Service”,模板选择 “Empty Ability”;
  3. 配置项目信息:
    • Project Name:ElectronAtomicService
    • Package Name:com.example.electronatomicservice(需与开发者联盟后台一致);
    • API Version:9+;
    • Ability Type:Service Ability(原子化服务核心能力)。

4.2 导入 Electron 打包产物

  1. 在鸿蒙项目的 main_pages 目录下,创建 electron 文件夹;
  2. 将 Electron 打包后的 electron-harmony-linux-x64 目录下的所有文件,复制到 electron 文件夹中;
  3. 在 electron 目录下创建 run.sh 脚本,用于启动 Electron 应用:

    bash

    运行

    #!/bin/sh
    cd $(dirname $0)
    ./electron-harmony
    
    给脚本添加执行权限:

    bash

    运行

    chmod +x run.sh
    

4.3 配置 HAP 包清单文件(config.json)

config.json 是鸿蒙应用的核心配置文件,需配置原子化服务的基本信息、权限、启动入口等。修改 entry/src/main/config.json

json

{
  "app": {
    "bundleName": "com.example.electronatomicservice",
    "vendor": "example",
    "version": {
      "code": 1000000,
      "name": "1.0.0"
    },
    "apiVersion": {
      "compatible": 9,
      "target": 9
    }
  },
  "module": {
    "package": "com.example.electronatomicservice",
    "name": "entry",
    "mainAbility": "com.example.electronatomicservice.MainAbility",
    "deviceTypes": ["phone", "tablet", "tv"], // 支持的终端类型
    "distro": {
      "deliveryWithInstall": false, // 原子化服务:免安装
      "moduleName": "entry",
      "moduleType": "entry",
      "installationFree": true // 启用免安装模式
    },
    "abilities": [
      {
        "name": "com.example.electronatomicservice.MainAbility",
        "type": "service",
        "visible": true,
        "skills": [
          {
            "entities": ["entity.system.service"],
            "actions": ["action.system.start"]
          }
        ],
        "backgroundModes": ["dataTransfer"],
        "launchType": "singleton"
      }
    ],
    "resources": {
      "rawFile": [
        {
          "path": "electron" // 导入的 Electron 资源
        }
      ]
    },
    "permissions": [
      {
        "name": "ohos.permission.EXECUTE_SHELL_COMMAND" // 执行 Shell 脚本权限
      }
    ]
  }
}

关键配置说明:

  • deliveryWithInstall: false:原子化服务无需随系统安装;
  • installationFree: true:启用免安装模式;
  • permissions:申请执行 Shell 脚本的权限(启动 Electron 应用需此权限);
  • rawFile:指定导入的 Electron 资源目录。

4.4 实现 Ability 启动逻辑

修改 entry/src/main/java/com/example/electronatomicservice/MainAbility.java,在 onStart 方法中启动 Electron 应用:

java

运行

package com.example.electronatomicservice;

import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.io.IOException;

public class MainAbility extends Ability {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.DEBUG, 0x00001, "ElectronAtomicService");
    private Process electronProcess;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        HiLog.info(LABEL, "Electron atomic service started");
        // 启动 Electron 应用
        new Thread(() -> {
            try {
                Context context = getContext();
                // 获取 Electron 资源路径
                String electronPath = context.getBundleResourceManager().getRawFileEntry("electron/run.sh").getFile().getPath();
                HiLog.info(LABEL, "Electron run path: " + electronPath);
                // 执行启动脚本
                ProcessBuilder pb = new ProcessBuilder(electronPath);
                pb.redirectErrorStream(true);
                electronProcess = pb.start();
                // 等待进程结束
                electronProcess.waitFor();
            } catch (IOException | InterruptedException e) {
                HiLog.error(LABEL, "Failed to start Electron app: " + e.getMessage());
            }
        }).start();
    }

    @Override
    public void onStop() {
        super.onStop();
        // 停止 Electron 进程
        if (electronProcess != null && isAlive(electronProcess)) {
            electronProcess.destroy();
            HiLog.info(LABEL, "Electron app stopped");
        }
    }

    private boolean isAlive(Process process) {
        try {
            process.exitValue();
            return false;
        } catch (IllegalThreadStateException e) {
            return true;
        }
    }
}

4.5 编译 HAP 包

  1. 在 DevEco Studio 中,点击 “Build → Build HAP (s)/APP (s) → Build HAP (s)”;
  2. 编译成功后,在 entry/build/outputs/hap/release 目录下生成 HAP 包(entry-release.hap);
  3. 验证 HAP 包:通过鸿蒙模拟器或真实设备安装 HAP 包,启动原子化服务,能正常打开 Electron 应用则适配成功。

注意:若出现 “权限不足” 错误,需在设备的 “设置 → 应用 → 权限管理” 中,给应用授予 “执行 Shell 命令” 权限;若启动失败,可通过 HiLog 日志排查问题(DevEco Studio 的 “Logcat” 面板查看)。

五、鸿蒙服务卡片集成:桌面展示与应用跳转

鸿蒙服务卡片是原子化服务的核心入口,支持在桌面展示应用核心功能,点击卡片可直接启动 Electron 应用。本节将实现一个 “数据统计” 类型的服务卡片,展示 Electron 应用的核心数据,并支持跳转至应用。

5.1 创建服务卡片

  1. 在 DevEco Studio 中,右键点击 entry/src/main 目录,选择 “New → Service Widget”;
  2. 配置卡片信息:
    • Widget Name:ElectronWidget
    • Widget Size:选择 “2x2”(桌面 2x2 网格大小);
    • Template:选择 “Default”。

5.2 设计卡片布局(widget_layout.xml)

修改 entry/src/main/resources/base/layout/widget_layout.xml,设计卡片 UI(展示应用名称、数据统计、跳转按钮):

xml

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:width="match_parent"
    ohos:height="match_parent"
    ohos:orientation="vertical"
    ohos:background_element="#f5f5f5">

    <!-- 卡片标题 -->
    <Text
        ohos:width="match_parent"
        ohos:height="40vp"
        ohos:text="Electron 原子化服务"
        ohos:text_size="18fp"
        ohos:text_weight="bold"
        ohos:gravity="center"
        ohos:background_element="#2f54eb"/>

    <!-- 数据统计展示 -->
    <DirectionalLayout
        ohos:width="match_parent"
        ohos:height="80vp"
        ohos:orientation="horizontal"
        ohos:gravity="center"
        ohos:top_margin="10vp">

        <Text
            ohos:width="wrap_content"
            ohos:height="wrap_content"
            ohos:text="今日访问:"
            ohos:text_size="16fp"
            ohos:text_color="#333"/>

        <Text
            ohos:id="$+id:visit_count"
            ohos:width="wrap_content"
            ohos:height="wrap_content"
            ohos:text="0"
            ohos:text_size="20fp"
            ohos:text_color="#2f54eb"
            ohos:left_margin="10vp"/>
    </DirectionalLayout>

    <!-- 跳转按钮 -->
    <Button
        ohos:id="$+id:open_app_btn"
        ohos:width="120vp"
        ohos:height="40vp"
        ohos:text="打开应用"
        ohos:text_size="16fp"
        ohos:background_element="$graphic:button_bg"
        ohos:gravity="center"
        ohos:top_margin="10vp"
        ohos:layout_alignment="center"/>

</DirectionalLayout>

创建按钮背景 entry/src/main/resources/base/graphic/button_bg.xml

xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
       ohos:shape="rectangle">
    <solid ohos:color="#2f54eb"/>
    <corners ohos:radius="20vp"/>
</shape>

5.3 实现卡片逻辑(WidgetProvider.java)

修改 entry/src/main/java/com/example/electronatomicservice/ElectronWidget.java,实现卡片数据更新与跳转逻辑:

java

运行

package com.example.electronatomicservice;

import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.WidgetProvider;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Text;
import ohos.agp.components.element.ShapeElement;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.util.Random;

public class ElectronWidget extends WidgetProvider {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.DEBUG, 0x00001, "ElectronWidget");
    private static final String ACTION_OPEN_APP = "com.example.electronatomicservice.ACTION_OPEN_APP";

    @Override
    public void onUpdate(Context context, Intent intent) {
        super.onUpdate(context, intent);
        HiLog.info(LABEL, "Widget onUpdate");
        // 更新卡片数据(模拟今日访问量)
        updateWidgetData(context);
        // 绑定按钮点击事件
        bindButtonClick(context);
    }

    // 更新卡片数据
    private void updateWidgetData(Context context) {
        Text visitCount = (Text) findComponentById(ResourceTable.Id_visit_count);
        if (visitCount != null) {
            int count = new Random().nextInt(1000);
            visitCount.setText(String.valueOf(count));
        }
    }

    // 绑定按钮点击事件:跳转至 Electron 应用
    private void bindButtonClick(Context context) {
        Button openBtn = (Button) findComponentById(ResourceTable.Id_open_app_btn);
        if (openBtn != null) {
            openBtn.setClickedListener(component -> {
                HiLog.info(LABEL, "Open Electron app from widget");
                // 启动原子化服务(即 MainAbility)
                Intent intent = new Intent();
                intent.setAction(ACTION_OPEN_APP);
                intent.setElementName(context.getBundleName(), MainAbility.class.getName());
                context.startAbility(intent);
            });
        }
    }
}

5.4 配置卡片清单(config.json)

在 config.json 的 abilities 节点下,添加服务卡片配置:

json

"abilities": [
  {
    "name": "com.example.electronatomicservice.MainAbility",
    // ... 原有配置
  },
  {
    "name": "com.example.electronatomicservice.ElectronWidget",
    "type": "widget",
    "visible": true,
    "widgetSize": ["2*2"],
    "metadata": [
      {
        "name": "ohos.widget.provider",
        "resource": "$profile:widget_profile"
      }
    ]
  }
]

创建卡片配置文件 entry/src/main/resources/base/profile/widget_profile.json

json

{
  "forms": [
    {
      "name": "ElectronWidget",
      "description": "Electron 原子化服务卡片",
      "src": "$layout:widget_layout",
      "window": {
        "designWidth": 720,
        "autoDesignWidth": true
      },
      "colorMode": "auto",
      "defaultDimension": "2*2",
      "supportDimensions": ["2*2"],
      "updateEnabled": true,
      "updateDuration": 3600, // 卡片数据更新周期(秒)
      "formVisibleNotify": true,
      "formTempCache": true
    }
  ]
}

5.5 验证服务卡片

  1. 编译鸿蒙项目,将 HAP 包安装到鸿蒙设备或模拟器;
  2. 在设备桌面长按空白处,选择 “添加卡片”,找到 “Electron 原子化服务卡片” 并添加;
  3. 卡片会显示模拟的访问量数据,点击 “打开应用” 按钮,能正常启动 Electron 应用则集成成功。

六、Electron 与鸿蒙系统的通信机制

为实现 Electron 应用与鸿蒙系统的数据交互(如卡片数据同步、系统状态获取),需打通两者的通信通道。本节将基于 node-ipc 和鸿蒙的 Intent 机制,实现双向通信。

6.1 Electron 端通信服务搭建

在 Electron 项目的 main.js 中,使用 node-ipc 启动 IPC 服务,接收鸿蒙系统的消息:

javascript

运行

const { app, BrowserWindow, ipcMain } = require('electron');
const ipc = require('node-ipc');

// 配置 IPC 服务
ipc.config.id = 'electron-harmony-ipc';
ipc.config.retry = 1500;
ipc.config.silent = true;

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
    }
  });
  mainWindow.loadFile('dist/index.html');

  // 启动 IPC 服务
  startIpcServer();
}

function startIpcServer() {
  ipc.serve(() => {
    console.log('Electron IPC server started');
    // 接收鸿蒙系统发送的消息
    ipc.server.on('message', (data, socket) => {
      console.log('Received message from HarmonyOS:', data);
      // 处理消息(如更新应用数据)
      mainWindow.webContents.send('harmony-message', data);
      // 回复鸿蒙系统
      ipc.server.emit(socket, 'response', { status: 'success', message: 'Message received' });
    });
  });
  ipc.server.start();
}

// 监听渲染进程发送的消息(用于向鸿蒙系统发送数据)
ipcMain.on('send-to-harmony', (event, data) => {
  console.log('Send message to HarmonyOS:', data);
  // 此处需结合鸿蒙端的 IPC 客户端,实现消息发送(下文讲解)
});

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

在 Electron 渲染进程(dist/index.html)中,添加消息接收与发送逻辑:

html

预览

<!DOCTYPE html>
<html>
<body>
  <h1>Electron 鸿蒙原子化服务适配Demo</h1>
  <div id="message"></div>
  <button onclick="sendMessageToHarmony()">向鸿蒙发送消息</button>

  <script>
    // 接收主进程转发的鸿蒙消息
    require('electron').ipcRenderer.on('harmony-message', (event, data) => {
      document.getElementById('message').innerText = `收到鸿蒙消息:${JSON.stringify(data)}`;
    });

    // 向鸿蒙系统发送消息
    function sendMessageToHarmony() {
      require('electron').ipcRenderer.send('send-to-harmony', {
        type: 'data_sync',
        content: 'Electron 应用数据'
      });
    }
  </script>
</body>
</html>

6.2 鸿蒙端 IPC 客户端实现

在鸿蒙项目的 MainAbility.java 中,添加 IPC 客户端逻辑,与 Electron 的 IPC 服务通信:

java

运行

package com.example.electronatomicservice;

import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class MainAbility extends Ability {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.DEBUG, 0x00001, "ElectronAtomicService");
    private Process electronProcess;
    private Socket ipcSocket;
    private OutputStreamWriter ipcWriter;
    private BufferedReader ipcReader;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        HiLog.info(LABEL, "Electron atomic service started");
        // 启动 Electron 应用
        startElectronApp();
        // 连接 Electron IPC 服务
        connectIpcServer();
    }

    // 启动 Electron 应用(原有逻辑)
    private void startElectronApp() {
        // ... 原有代码
    }

    // 连接 Electron IPC 服务(Electron 端 IPC 服务默认端口 8080)
    private void connectIpcServer() {
        new Thread(() -> {
            try {
                // 等待 Electron IPC 服务启动(延迟 3 秒)
                Thread.sleep(3000);
                // 连接 Electron IPC 服务(IP 为本地回环地址,端口 8080)
                ipcSocket = new Socket("127.0.0.1", 8080);
                ipcWriter = new OutputStreamWriter(ipcSocket.getOutputStream());
                ipcReader = new BufferedReader(new InputStreamReader(ipcSocket.getInputStream()));
                HiLog.info(LABEL, "Connected to Electron IPC server");

                // 向 Electron 发送消息
                sendMessageToElectron("{\"type\":\"system_info\",\"content\":\"HarmonyOS 4.0\"}");

                // 接收 Electron 回复的消息
                String response;
                while ((response = ipcReader.readLine()) != null) {
                    HiLog.info(LABEL, "Received response from Electron: " + response);
                    // 更新服务卡片数据
                    updateWidgetData(response);
                }
            } catch (IOException | InterruptedException e) {
                HiLog.error(LABEL, "IPC connection failed: " + e.getMessage());
            }
        }).start();
    }

    // 向 Electron 发送消息
    private void sendMessageToElectron(String message) {
        try {
            ipcWriter.write(message + "\n");
            ipcWriter.flush();
            HiLog.info(LABEL, "Sent message to Electron: " + message);
        } catch (IOException e) {
            HiLog.error(LABEL, "Failed to send message: " + e.getMessage());
        }
    }

    // 更新服务卡片数据
    private void updateWidgetData(String data) {
        // 此处通过鸿蒙的 FormController 更新卡片数据,参考官方文档:
        // https://developer.harmonyos.com/cn/docs/documentation/doc-guides/form_update-0000001157358070
    }

    @Override
    public void onStop() {
        super.onStop();
        // 关闭 IPC 连接
        try {
            if (ipcWriter != null) ipcWriter.close();
            if (ipcReader != null) ipcReader.close();
            if (ipcSocket != null) ipcSocket.close();
        } catch (IOException e) {
            HiLog.error(LABEL, "Failed to close IPC connection: " + e.getMessage());
        }
        // 停止 Electron 进程(原有逻辑)
        // ...
    }
}

6.3 通信验证

  1. 重新打包 Electron 应用,更新鸿蒙项目中的 electron 目录;
  2. 安装 HAP 包并启动原子化服务;
  3. 鸿蒙端会向 Electron 发送系统信息,Electron 应用的页面会显示收到的消息;
  4. 点击 Electron 应用中的 “向鸿蒙发送消息” 按钮,鸿蒙端的日志会打印收到的消息,说明双向通信成功。

进阶方案:使用鸿蒙的 DataAbility 或 DistributedData 实现分布式数据同步,支持多终端间的 Electron 应用数据共享。参考 鸿蒙分布式数据管理文档

七、实战案例:Electron 笔记工具适配鸿蒙原子化服务

基于以上流程,我们实现一个完整的实战案例:将一个简单的 Electron 笔记工具(支持新建、保存笔记)适配为鸿蒙原子化服务,并集成服务卡片展示笔记列表。

7.1 案例需求

  1. Electron 笔记工具核心功能:新建笔记、保存笔记、查看笔记列表;
  2. 鸿蒙服务卡片:展示最近 3 条笔记标题,点击卡片跳转至笔记工具;
  3. 通信功能:卡片数据与 Electron 应用实时同步。

7.2 核心代码实现

(1)Electron 笔记工具核心代码(main.js)

javascript

运行

const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const ipc = require('node-ipc');
const fs = require('fs');
const path = require('path');

// 笔记存储路径
const NOTE_DIR = path.join(app.getPath('userData'), 'notes');
if (!fs.existsSync(NOTE_DIR)) fs.mkdirSync(NOTE_DIR);

// IPC 服务配置(同上节)
ipc.config.id = 'electron-note-ipc';
ipc.config.retry = 1500;
ipc.config.silent = true;

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
    }
  });
  mainWindow.loadFile('dist/index.html');
  startIpcServer();
}

// 读取笔记列表
function getNoteList() {
  const files = fs.readdirSync(NOTE_DIR);
  return files.map(file => {
    const content = fs.readFileSync(path.join(NOTE_DIR, file), 'utf8');
    return {
      title: file.replace('.txt', ''),
      content: content.substring(0, 30) + '...',
      time: fs.statSync(path.join(NOTE_DIR, file)).mtime.toLocaleString()
    };
  }).sort((a, b) => new Date(b.time) - new Date(a.time)).slice(0, 3); // 取最近 3 条
}

// IPC 服务(新增笔记列表同步逻辑)
function startIpcServer() {
  ipc.serve(() => {
    ipc.server.on('get_note_list', (data, socket) => {
      const noteList = getNoteList();
      ipc.server.emit(socket, 'note_list', noteList);
    });
  });
  ipc.server.start();
}

// 保存笔记(接收渲染进程消息)
ipcMain.on('save_note', (event, { title, content }) => {
  const filePath = path.join(NOTE_DIR, `${title}.txt`);
  fs.writeFileSync(filePath, content);
  event.reply('save_success', true);
  // 同步更新卡片数据(通过 IPC 通知鸿蒙端)
  if (ipc.server.clients.length > 0) {
    ipc.server.broadcast('note_updated', getNoteList());
  }
});

app.whenReady().then(createWindow);
// ... 其他生命周期代码
(2)鸿蒙服务卡片数据同步代码(MainAbility.java)

java

运行

// 在 connectIpcServer 方法中,新增接收笔记列表的逻辑
private void connectIpcServer() {
    new Thread(() -> {
        try {
            Thread.sleep(3000);
            ipcSocket = new Socket("127.0.0.1", 8080);
            ipcWriter = new OutputStreamWriter(ipcSocket.getOutputStream());
            ipcReader = new BufferedReader(new InputStreamReader(ipcSocket.getInputStream()));

            // 向 Electron 请求笔记列表
            sendMessageToElectron("{\"type\":\"get_note_list\"}");

            // 接收笔记列表
            String response;
            while ((response = ipcReader.readLine()) != null) {
                HiLog.info(LABEL, "Received note list: " + response);
                // 更新卡片数据
                updateWidgetNoteList(response);
            }
        } catch (IOException | InterruptedException e) {
            HiLog.error(LABEL, "IPC connection failed: " + e.getMessage());
        }
    }).start();
}

// 更新卡片笔记列表
private void updateWidgetNoteList(String noteListJson) {
    // 解析 JSON 数据(使用 Gson 库)
    Gson gson = new Gson();
    List<Note> noteList = gson.fromJson(noteListJson, new TypeToken<List<Note>>(){}.getType());
    
    // 通过 FormController 更新卡片 UI
    // ... 具体实现参考鸿蒙官方文档
}

// 笔记实体类
static class Note {
    private String title;
    private String content;
    private String time;

    // getter/setter 方法
}

7.3 案例效果验证

  1. 安装 HAP 包后,添加服务卡片,卡片会显示最近 3 条笔记标题;
  2. 启动 Electron 笔记工具,新建并保存笔记,卡片数据会实时更新;
  3. 点击卡片中的笔记标题,会跳转至 Electron 应用并打开对应笔记。

八、常见问题排查与解决方案

8.1 Electron 应用启动失败

  • 问题 1:run.sh 脚本无执行权限 → 解决方案:chmod +x run.sh
  • 问题 2:鸿蒙设备缺少 Linux 运行环境 → 解决方案:确保设备系统版本为 HarmonyOS 3.0+,且已安装 libgtk-3-0 等依赖;
  • 问题 3:权限不足 → 解决方案:在 config.json 中添加 ohos.permission.EXECUTE_SHELL_COMMAND 权限,并在设备中授予。

8.2 服务卡片不显示或数据不更新

  • 问题 1:卡片配置错误 → 解决方案:检查 config.json 中的 widget 配置,确保 src 路径、widgetSize 正确;
  • 问题 2:卡片更新周期过长 → 解决方案:在 widget_profile.json 中减小 updateDuration(如 60 秒);
  • 问题 3:IPC 通信失败 → 解决方案:检查 Electron 端 IPC 服务是否启动,端口是否被占用。

8.3 打包体积过大

  • 问题:Electron 打包产物超过 10MB → 解决方案:
    1. 进一步裁剪依赖(如移除 node_modules 中的 docstests 目录);
    2. 使用 electron-builder 的 asarUnpack 配置,仅解压必要文件;
    3. 压缩 Electron 可执行文件(使用 upx 工具)。

8.4 分布式流转失败

  • 问题:应用无法在多终端间流转 → 解决方案:
    1. 确保所有设备登录同一华为账号,且处于同一网络;
    2. 在 config.json 中添加支持的设备类型(如 tvtablet);
    3. 使用鸿蒙的 DistributedAbility 替代 ServiceAbility

九、总结与展望

本文详细讲解了 Electron 应用适配鸿蒙原子化服务的完整流程,包括 轻量级打包、HAP 包封装、服务卡片集成、跨进程通信 四大核心环节,并通过实战案例验证了方案的可行性。通过适配,Electron 应用不仅能保留 Web 技术栈的跨平台优势,还能借助鸿蒙原子化服务的 “免安装、全场景、分布式” 特性,拓展应用的使用场景和分发渠道。

未来展望:

  1. 随着鸿蒙生态的完善,Electron 可能会推出官方鸿蒙适配方案,简化打包与集成流程;
  2. 原子化服务将支持更多终端类型(如智能手表、车机),Electron 应用可借助鸿蒙实现全场景覆盖;
  3. 分布式数据同步、跨终端协作等高级特性,将成为 Electron 与鸿蒙适配的重点方向。

如果你在适配过程中遇到问题,欢迎在评论区留言交流!也可参考以下官方资源进一步学习:

Logo

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

更多推荐