• 进程间通讯(IPC)工作流程
    • 发送消息
      • 主进程向面板发送消息
      • 面板向主进程发送消息
      • 其他消息发送方法
    • 接收消息
      • 面板渲染进程消息监听
      • 主进程消息监听
      • 其他消息监听方式
    • 向消息来源发送回调
      • 处理超时

    进程间通讯(IPC)工作流程

    关于扩展包进程间通讯(以下简称 IPC)的基本概念,请先阅读 IPC 简介。

    我们前面介绍了主进程中的 入口程序 和渲染进程中的 面板程序 的基本声明方法和交互方式,接下来我们将结合实际需求介绍两种进程间通讯的详细工作流程。

    本节提及的所有相关 API 均可查询 Editor.Ipc 主进程 API 和 Editor.Ipc 渲染进程 API。

    发送消息

    主进程向面板发送消息

    在主进程中,主要使用

    Editor.Ipc.sendToPanel('panelID', 'message' [, …args, callback, timeout])

    接口向特定面板发送消息。对于目前支持的单面板插件来说:

    • panelID 面板 ID,对于单面板扩展包来说,面板 ID 就是插件的包名,如 foobar
    • message 是 IPC 消息的全名,如 do-some-work,我们推荐在定义 IPC 消息名时使用 - 来连接单词,而不是使用驼峰或下划线。
    • 可选 args,从第三个参数开始,可以定义数量不定的多个传参,用于传递更具体的信息到面板进程。
    • 可选 callback,在传参后面可以添加回调方法,在面板进程中接受到 IPC 消息后可以选择向主进程发送回调,并通过 callback 回调方法进行处理。回调方法的参数第一个是 error(如果没有错误则传入 null),之后才是传参。
    • 可选 timeout,回调超时,只能配合回调方法一起使用,如果规定了超时,在消息发送后的一定时间内没有接到回调方法,就会触发超时错误。如果不指定超时,则默认的超时设置是 5000 毫秒。

    面板向主进程发送消息

    Editor.Ipc.sendToMain('message', [, …args, callback, timeout])

    sendToPanel 相比,除了缺少第一个面板 ID 参数之外,其他参数的意义和用法完全相同。

    其他消息发送方法

    主进程对面板和面板对主进程是最常见的两种消息发送方式,但实际上 IPC 不局限于不同的两类进程之间,我们可以把消息发送方式做以下归类:

    • 任意进程对主进程 Editor.Ipc.sendToMain
    • 任意进程对面板 Editor.Ipc.sendToPanel
    • 任意进程对编辑器主窗口(也就是对主窗口里的所有渲染进程广播)Editor.Ipc.sendToMainWin
    • 任意进程对所有窗口(对包括弹出窗口在内的所有窗口渲染进程广播)Editor.Ipc.sendToWins
    • 任意进程对所有进程广播 Editor.Ipc.sendToAll
      上述方法在两种进程里写法都是一致的,只要注意消息接收的对象是在渲染进程还是主进程,并选择对应的方法即可。详细的接口用法请参考上文的描述和本文最上面的 IPC 接口文档链接。

    注意: 由于通讯基于 Electron 的底层 IPC 实现,所以切记传输的数据不可以包含原生对象,否则可能导致进程崩溃或者内存暴涨。推荐只传输纯 JSON 对象。

    接收消息

    要在主进程或渲染进程中接受 IPC 消息,最简单的办法是在声明对象的 messages 字段中注册以 IPC 消息为名的消息处理方法。

    面板渲染进程消息监听

    1. //packages/foobar/panel/index.js
    2. Editor.Panel.extends({
    3. //...
    4. messages: {
    5. 'my-message': function (event, ...args) {
    6. //do some work
    7. }
    8. }
    9. });

    主进程消息监听

    1. //packages/foobar/main.js
    2. module.exports = {
    3. //...
    4. messages: {
    5. 'my-message': function (event, ...args) {
    6. //do some work
    7. }
    8. }
    9. }

    注册监听消息时,我们使用的消息名是省略了扩展包名的短命名,上述消息短名 my-message 在发送时应该是 Editor.Ipc.sendToPanel('foobar:my-message')Editor.Ipc.sendToMain('foobar:my-messages')

    可以看到主进程和渲染进程中监听 IPC 消息的函数声明方式是一致的,传入的第一个参数是一个 event 对象,我们可以通过这个对象发送回调。

    其他消息监听方式

    除了在 messages 字段内注册之外还可以使用 Electron 的 Ipc 消息接口来监听,形式上更灵活:

    渲染进程中:

    1. require('electron').ipcRenderer.on('foobar:message', function(event, args) {});

    主进程中:

    1. require('electron').ipcMain.on('foobar:message', function(event, args) {});

    关于 Electron 的 IPC 接口可以参考 Electron API: ipcMainElectron API: ipcRenderer。

    向消息来源发送回调

    假如我们从主进程发送了一个消息:

    1. //packages/foobar/main.js
    2. Editor.Ipc.sendToPanel('foobar', 'greeting', 'How are you?', function (error, answer) {
    3. Editor.log(answer);
    4. });

    在面板监听消息的方法中,我们可以使用 event.reply 来发送回调:

    1. //packages/foobar/panel/index.js
    2. Editor.Panel.extends({
    3. //...
    4. messages: {
    5. 'greeting': function (event, question) {
    6. console.log(question); //How are you?
    7. if (event.reply) {
    8. //if no error, the first argument should be null
    9. event.reply(null, 'Fine, thank you!');
    10. }
    11. }
    12. }
    13. });

    注意 event.reply 第一个参数是报错,没有错误时应该传入 null,此外建议总是检查 event.reply 是否存在,如果发送消息时参数中不包含回调方法,则 event.reply 的检查将返回 undefined,这种情况下调用 event.reply 会产生错误。

    处理超时

    发送消息时的最后一个参数是超时时限,单位是毫秒,如果未指定超时时限,则使用默认的 5000 ms 超时限制。

    如果要取消超时限制,最后一次参数应该传入 -1,这种情况下应该靠其他逻辑保证回调必将触发。

    从消息发送开始,在超过规定的时限后仍然没有接到消息监听方法中返回的回调的话,就会收到系统发送的超时错误回调:

    1. Editor.Ipc.sendToMain('foobar:greeting', function (error, answer) {
    2. if ( error.code === 'ETIMEOUT' ) { //check the error code to confirm a timeout
    3. Editor.error('Timeout for ipc message foobar:greeting');
    4. return;
    5. }
    6. Editor.log(answer);
    7. });