引言
随着华为鸿蒙OS(HarmonyOS)的快速发展,越来越多的开发者开始关注如何将现有技术栈与鸿蒙生态进行整合。而Electron作为构建跨平台桌面应用的成熟框架,能否与鸿蒙系统产生"化学反应"?本文将探索鸿蒙设备与Electron应用的协同工作模式,通过实际代码案例展示如何实现桌面应用与鸿蒙设备的无缝通信。
重要提示:鸿蒙OS本身并不直接支持运行Electron应用(Electron基于Chromium+Node.js,而鸿蒙有自己的应用框架),但我们可以利用网络通信实现两者间的协同工作。本文重点介绍这种跨平台协作模式。
一、技术背景
1.1 鸿蒙OS网络能力
鸿蒙OS 3.0+ 提供了强大的网络通信能力:
- 支持HTTP/HTTPS协议
- 可创建本地HTTP服务器
- 具备WebSocket通信能力
- 支持局域网设备发现
1.2 Electron的网络优势
Electron作为桌面应用框架,天然具备:
- 完整的Node.js后端能力
- 便捷的HTTP客户端功能
- 丰富的UI组件库
- 跨Windows/macOS/Linux平台
1.3 协作模式
https://img-blog.csdnimg.cn/direct/8a7b8c9d0e1f4e4f8f4e4f4f4f4f4f4f4.png
核心思路:
鸿蒙设备作为服务提供方运行HTTP服务,Electron应用作为客户端发起请求,通过局域网实现数据交互。
二、实战案例:设备状态监控系统
让我们实现一个简单的应用场景:
通过Electron桌面应用实时监控鸿蒙设备的电池状态和系统信息
2.1 鸿蒙端实现(服务提供方)
步骤1:创建鸿蒙项目
- 打开DevEco Studio
- 创建新项目:
Create > Empty Ability - 选择API Version 9+(推荐API 10)
步骤2:添加网络权限
在module.json5中添加网络权限:
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.GET_NETWORK_INFO"
}
]
}
}
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.GET_NETWORK_INFO"
}
]
}
}
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.GET_NETWORK_INFO"
}
]
}
}
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.GET_NETWORK_INFO"
}
]
}
}
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.GET_NETWORK_INFO"
}
]
}
}
步骤3:实现HTTP服务
创建HttpServer.ets文件:
import http from '@ohos.net.http'; import battery from '@ohos.batteryManager'; export class DeviceServer { private server: http.HttpServer; constructor() { this.server = http.createHttpServer(); } start(port: number = 8080) { // 配置请求处理 this.server.on('request', (request: http.HttpRequest, response: http.HttpResponse) => { console.log(`Request received: ${request.url}`); // 处理不同API端点 if (request.url === '/api/status') { this.handleStatusRequest(response); } else if (request.url === '/api/battery') { this.handleBatteryRequest(response); } else { this.sendResponse(response, 404, 'Not Found'); } }); // 启动服务器 this.server.listen(port, () => { console.log(`HTTP server running on port ${port}`); }); } private handleStatusRequest(response: http.HttpResponse) { const status = { deviceName: 'HarmonyOS Device', osVersion: 'HarmonyOS 4.0', uptime: Math.floor(process.uptime() * 1000), timestamp: new Date().toISOString() }; this.sendJsonResponse(response, status); } private handleBatteryRequest(response: http.HttpResponse) { try { const level = battery.getBatteryLevel(); const status = battery.getChargingStatus(); const batteryInfo = { level: level * 100, isCharging: status === battery.ChargingStatus.CHARGING, health: battery.getHealthStatus(), temperature: battery.getBatteryTemperature() / 10 }; this.sendJsonResponse(response, batteryInfo); } catch (err) { console.error('Battery error:', err); this.sendResponse(response, 500, 'Failed to get battery info'); } } private sendJsonResponse(response: http.HttpResponse, data: any) { response.writeHeader(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); response.end(JSON.stringify(data)); } private sendResponse(response: http.HttpResponse, code: number, message: string) { response.writeHeader(code, { 'Content-Type': 'text/plain' }); response.end(message); } }步骤4:在MainAbility中启动服务
// MainAbility.ets import { DeviceServer } from './HttpServer'; @Entry @Component struct MainAbility { private server: DeviceServer = new DeviceServer(); build() { // UI代码... } aboutToAppear() { // 启动HTTP服务 this.server.start(8080); console.log('Device server started'); // 保持后台运行(可选) this.keepAlive(); } private keepAlive() { // 实现后台保活逻辑(根据业务需求) console.log('Keeping server alive...'); } }https://img-blog.csdnimg.cn/direct/3a4b5c6d7e8f9e4f8f4e4f4f4f4f4f4f4.png
2.2 Electron端实现(客户端)
步骤1:创建Electron项目
mkdir harmony-electron cd harmony-electron npm init -y npm install electron axios @types/node步骤2:创建主进程文件
main.js:
const { app, BrowserWindow, ipcMain } = require('electron')
const axios = require('axios')
const path = require('path')
// 鸿蒙设备IP(需替换为实际IP)
const HARMONY_DEVICE_IP = '192.168.1.100'
function createWindow() {
const win = new BrowserWindow({
width: 1000,
height: 700,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true
}
})
win.loadFile('index.html')
// 自动刷新设备数据
setInterval(() => {
fetchDeviceStatus(win)
fetchBatteryInfo(win)
}, 5000)
}
// 获取设备状态
async function fetchDeviceStatus(window) {
try {
const response = await axios.get(`http://${HARMONY_DEVICE_IP}:8080/api/status`)
window.webContents.send('device-status', response.data)
} catch (error) {
console.error('Status fetch error:', error.message)
window.webContents.send('device-status', {
error: '无法连接设备',
timestamp: new Date().toISOString()
})
}
}
// 获取电池信息
async function fetchBatteryInfo(window) {
try {
const response = await axios.get(`http://${HARMONY_DEVICE_IP}:8080/api/battery`)
window.webContents.send('battery-info', response.data)
} catch (error) {
console.error('Battery fetch error:', error.message)
}
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
// 处理手动刷新请求
ipcMain.on('refresh-data', () => {
const windows = BrowserWindow.getAllWindows()
if (windows.length > 0) {
fetchDeviceStatus(windows[0])
fetchBatteryInfo(windows[0])
}
})
const { app, BrowserWindow, ipcMain } = require('electron')
const axios = require('axios')
const path = require('path')
// 鸿蒙设备IP(需替换为实际IP)
const HARMONY_DEVICE_IP = '192.168.1.100'
function createWindow() {
const win = new BrowserWindow({
width: 1000,
height: 700,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true
}
})
win.loadFile('index.html')
// 自动刷新设备数据
setInterval(() => {
fetchDeviceStatus(win)
fetchBatteryInfo(win)
}, 5000)
}
// 获取设备状态
async function fetchDeviceStatus(window) {
try {
const response = await axios.get(`http://${HARMONY_DEVICE_IP}:8080/api/status`)
window.webContents.send('device-status', response.data)
} catch (error) {
console.error('Status fetch error:', error.message)
window.webContents.send('device-status', {
error: '无法连接设备',
timestamp: new Date().toISOString()
})
}
}
// 获取电池信息
async function fetchBatteryInfo(window) {
try {
const response = await axios.get(`http://${HARMONY_DEVICE_IP}:8080/api/battery`)
window.webContents.send('battery-info', response.data)
} catch (error) {
console.error('Battery fetch error:', error.message)
}
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
// 处理手动刷新请求
ipcMain.on('refresh-data', () => {
const windows = BrowserWindow.getAllWindows()
if (windows.length > 0) {
fetchDeviceStatus(windows[0])
fetchBatteryInfo(windows[0])
}
})
步骤3:创建预加载脚本
preload.js:
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
onStatusUpdate: (callback) => ipcRenderer.on('device-status', callback),
onBatteryUpdate: (callback) => ipcRenderer.on('battery-info', callback),
refreshData: () => ipcRenderer.send('refresh-data')
})
步骤4:创建UI界面
index.html:
<!DOCTYPE html>
window.electronAPI.onStatusUpdate((event, data) => {
if (data.error) {
deviceStatusEl.textContent = '离线';
deviceStatusEl.className = 'info-value error';
return;
}
deviceStatusEl.textContent = '在线';
deviceStatusEl.className = 'info-value';
lastUpdateEl.textContent = new Date().toLocaleTimeString();
deviceNameEl.textContent = data.deviceName;
osVersionEl.textContent = data.osVersion;
// 转换运行时间为可读格式
const uptimeMs = data.uptime;
const seconds = Math.floor((uptimeMs / 1000) % 60);
const minutes = Math.floor((uptimeMs / (1000 * 60)) % 60);
const hours = Math.floor((uptimeMs / (1000 * 60 * 60)) % 24);
uptimeEl.textContent = `${hours}h ${minutes}m ${seconds}s`;
});
// 处理电池信息更新
window.electronAPI.onBatteryUpdate((event, data) => {
if (data.error) return;
// 电量显示
batteryLevelEl.textContent = `${data.level.toFixed(1)}%`;
batteryFillEl.style.width = `${data.level}%`;
// 充电状态
if (data.isCharging) {
chargingStatusEl.textContent = '充电中';
chargingStatusEl.className = 'info-value charging';
} else {
chargingStatusEl.textContent = '未充电';
chargingStatusEl.className = 'info-value';
}
// 温度
temperatureEl.textContent = data.temperature.toFixed(1);
});
// 手动刷新按钮
refreshBtn.addEventListener('click', () => {
window.electronAPI.refreshData();
refreshBtn.textContent = '刷新中...';
setTimeout(() => {
refreshBtn.textContent = '手动刷新数据';
}, 1000);
});
});
</script>
</body>
</html>
步骤5:添加启动脚本
在package.json中添加:
{
"scripts": {
"start": "electron ."
}
}
步骤6:运行Electron应用
npm starthttps://img-blog.csdnimg.cn/direct/9a8b7c6d5e4f3e4f8f4e4f4f4f4f4f4f4.png
三、关键问题与解决方案
3.1 设备IP自动发现
手动输入IP不友好,可通过以下方式改进:
鸿蒙端广播服务:
// 在HttpServer中添加 import wifi from '@ohos.wifi'; private broadcastService() { const ssid = wifi.getWifiSsid(); // 获取当前WiFi名称 const ip = wifi.getIpAddress(); // 获取设备IP // 通过UDP广播服务信息(简化示例) const broadcastMsg = `HARMONY_DEVICE:${ssid}:${ip}:8080`; // 实现UDP广播逻辑... }Electron端自动发现:
// 添加设备发现功能
function discoverHarmonyDevices() {
const dgram = require('dgram');
const client = dgram.createSocket('udp4');
client.bind(() => {
client.setBroadcast(true);
const message = Buffer.from('DISCOVER_HARMONY');
client.send(message, 0, message.length, 4000, '255.255.255.255');
});
client.on('message', (msg, rinfo) => {
if (msg.toString().startsWith('HARMONY_DEVICE')) {
const [, , ip, port] = msg.toString().split(':');
console.log(`Found device at ${ip}:${port}`);
// 更新UI显示发现的设备
}
});
}
3.2 安全性增强
- 添加认证机制:在鸿蒙端实现简单的Token验证
- 使用HTTPS:配置自签名证书实现安全通信
- 限制IP访问:只允许特定IP范围访问
// 鸿蒙端添加Token验证 private handleStatusRequest(response: http.HttpResponse, request: http.HttpRequest) { const token = request.header['x-auth-token']; if (token !== 'YOUR_SECURE_TOKEN') { this.sendResponse(response, 401, 'Unauthorized'); return; } // ...继续处理 }3.3 跨域问题处理
在鸿蒙HTTP响应中添加CORS头:
response.writeHeader(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST' });四、应用场景拓展
- 设备调试工具:开发专业的鸿蒙设备调试桌面应用
- 多屏协同:实现桌面与鸿蒙设备的深度交互
- IoT管理平台:统一管理多个鸿蒙IoT设备
- 开发辅助工具:如资源管理、日志查看等
https://img-blog.csdnimg.cn/direct/1a2b3c4d5e6f7e4f8f4e4f4f4f4f4f4f4.png
五、总结
虽然鸿蒙OS与Electron属于不同的技术生态,但通过网络通信这一桥梁,我们可以实现:
- 高效的跨平台协作
- 桌面应用与移动设备的深度整合
- 复用现有Electron技术栈
这种模式特别适合需要桌面管理界面+鸿蒙设备的场景,既发挥了Electron在桌面端的优势,又充分利用了鸿蒙设备的移动特性。
关键收获:
- 鸿蒙可作为HTTP服务提供方
- Electron可作为功能强大的客户端
- 通过REST API实现数据互通
- 适用于设备监控、调试工具等场景
六、资源推荐
- 鸿蒙官方文档
- Electron官方文档
- 鸿蒙网络开发指南
- Electron与Node.js集成