JavaScript
Electron

ElectronのIPCをまとめる

More than 1 year has passed since last update.

前書き

Electronには、メインプロセスとレンダープロセスでプロセス間通信を行うIPCモジュールがあります。
デスクトップアプリを作るにあたり、IPCモジュールおよび各プロセスの動きというのは、とても大事なのでまとめてみました。
もしお手元にソースがない場合は、僕の記事で多用しているReactTodoのElectron版を利用してくだされば幸いです。

https://github.com/gcmae/react-todo/tree/react-redux-electron

IPC-プロセス間通信-

Electronには、二つのプロセスがあります。

  • アプリケーションを司るメインプロセス
  • 画面を司るレンダープロセス

メインプロセスでは、アプリケーションの起動から終了、画面(レンダープロセス)の作成から終了まで、アプリが行う動作全てを補っています。
メインプロセスは、アプリを司るため一つのアプリにつき一つしか動作できません。

対するレンダープロセスは、HTML、ざっくり言うと画面を司っており、今までの記事で言うとReactが動作している部分をレンダープロセスと言います。
画面ごとのプロセスのため、メインプロセスとは違い、複数動作できます。
例えば、メインプロセスからレンダープロセスに通信がしたい時
レンダープロセスからメインプロセスの処理を呼びたい時
そんな時に用いるのが、プロセス間通信、IPCです。

IPCの使い方

IPCでは、非同期通信同期通信が利用できます。
同期通信の場合、通信中はレンダープロセスが停止、スリープの状態になります。

モジュールのインポート

ipcを利用するにはipcモジュールをインポートする必要があります。
Electronの中に含まれているので、インストール等はいりません。
ただ、ipcモジュールは
メインプロセス用のモジュール
レンダープロセス用のモジュールがあるので、それぞれインポートします。

メインプロセス

メインプロセスには、ipcMainをインポートします。

index.js
// Electron
import {
  app,
  ipcMain, // <- メイン用のipc
  BrowserWindow
} from 'electron'

レンダープロセス

レンダープロセスには、ipcRendererをインポートします。

app.jsx
import {
  ipcRenderer // <- レンダー用のipc
} from 'electron'

非同期通信

では実際、ipcの非同期を用いて、メッセージを表示してみます。

レンダープロセス

app.jsx
  AsyncMessage() {
    ipcRenderer.send('message', 'ping')
    ipcRenderer.on('reply', (event, arg) => {
      console.log(arg)
    })
  }

メインプロセス

index.js
ipcMain.on('message', (event, arg) => {
  console.log(arg)
  event.sender.send('reply', 'pong');
})

実行して、AsyncMessageを呼ぶと以下の結果になります。

スクリーンショット 2017-05-26 17.15.55.png

上はターミナル上(メインプロセスによる)のログ
下はElectronのログです

動きとして早口に述べると

レンダープロセスのipcRenderer.sendでチャンネルmessageにpingを送信し、メインプロセスのチャンネルmessageでpingを受け取ってlogに表示し、受信元にチャンネルreplyでpongを送り、レンダープロセスのチャンネルreplyでpongを受け取り、logに表示

以上の処理を行なっています(説明が下手ですみません…)
チャンネルというのは、ipcの通信帯のようなもので
sendとonの第1引数です。
同じ文字列がsendとonを結んでいるので、今回の場合、sendのmessageとonのmessageがつながっているため
レンダープロセスのsendの先は同じチャンネルであるメインのonになるわけです。

処理自体は非同期となっており、メインからsendが帰ってくるまでの間は他の処理は進んでいます。

チャンネルreplyのsendがメインプロセスから送られてレンダープロセスのチャンネルreplyのonが呼ばれます。

同期通信

同期通信の場合、基本的に非同期と変わる部分はありません。
ただ、メインプロセスからリプライを待つわけではないので、メインプロセスにsendは記述する必要がありません。

レンダープロセス

app.jsx
  SyncMessage() {
    const result = ipcRenderer.sendSync('sync-message', 'ping')
    console.log(result)
  }

メインプロセス

index.js
ipcMain.on('sync-message', (event, arg) => {
  console.log(arg)
  event.returnValue = 'pong';
})

非同期の際はsendと記述していましたが、同期通信の場合はsendSyncを使います。
そうすることでipc通信は同期通信となり、メインプロセス側のreturnValueがない限り処理は止まります。
同期通信のため、resultにはpongが代入され、undefinedになることなくその次でlogが出せます。

後書き

Electronの二つのプロセス、IPCによる非同期、同期通信
まとめてみましたが、書き方はとても簡単なのに処理はなかなかややこしいです…
ただ、ここを理解できれば、Electronを使ってデスクトップアプリを作る際にいろいろと幅が広がる気がします。
お読みいただき、ありがとうございました!