Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
53
Help us understand the problem. What is going on with this article?
@nullpointer_t

【Electron連載】第4回 基本編-メイン/レンダラープロセスの話

More than 3 years have passed since last update.

↑【Electron連載(目次)】Electronでアプリ完成までのメモ

やっぱり、基本編を始める前にElectronのプロセスについては書いておかんと後で詰まる。
ということで、早速スタートや。

Electronで使われているプロセスのアーキテクチャ

プロセスについての良い説明と画像がったから紹介しておくわ。
00_s.gif
こちらから拝借させてもらったで。
http://www.buildinsider.net/enterprise/electron/01

メインプロセス

まずはメインプロセスな。
Electron(っていうかNode.jsか)のプロジェクトのフォルダにあるpackage.jsonの中身で
mainで定義されているファイルを実行するプロセスがメインプロセスやで。
メインというだけあって、このメインプロセスが終了=アプリケーションの終了ということやな。

レンダラープロセス

次はレンダラープロセスな。
いわゆる、画面ごとに作成されるプロセスの事。
メインプロセスからxxxx.htmlを読み込んで画面を表示したら、その画面自体が一つのプロセスで動作しているってことやで。
メインプロセスからいくらでも作ることが出来るで。

ここでポイント✌

Electronで利用できるAPIにはメインプロセスで使えるもの、レンダラープロセスで使えるもの、両方で使えるものあるさかい注意な。
というわけで、次回以降でAPIの説明の際にはどっちか明記する事にする。

プロセス間通信

んで、ここからが本題。
Electronでウィンドウ間のデータ通信はどうやるの?って時にプロセス間通信を利用するんやで。
まずは、Electronのプロセス間通信のルールがあるから気を付けてや。

  • プロセス間通信はメインプロセスとレンダラープロセスの間のみ可能。
  • レンダラープロセスとレンダラープロセスの通信は不可。
  • レンダラープロセスからはメインプロセスが一つしか存在しないため、宛先不要。
  • メインプロセスからはどのレンダラープロセスへ通信するか宛先が必要。
  • レンダラープロセスからはメインプロセスへは同期・非同期通信が可能
  • メインプロセスからレンダラープロセスへは非同期通信のみ可能。

んじゃ、さっき言った、ウィンドウ間(レンダラープロセス間)はどうするの?
という場合は・・・。

一旦、メインプロセスを間に入れる事によって通信を可能にするってこと。
レンダラープロセス
    
メインプロセス
    
レンダラープロセス
ということやね。

ipc(Inter-process Communication:プロセス間通信)の使い方

プロセス間通信を使う場合はメインプロセスとレンダラープロセスでお作法があるみたいやからそれに準じてやるで。

レンダラープロセス→メインプロセス。

まずは、このパターン。
アプリケーション作成していたらまず、ここからスタートすることになると思うで。

非同期通信

renderer-procss.ts
// レンダラープロセスでやりとりするipcRenderer
const {ipcRenderer} = require('electron');
// asynchronous-messageチャンネルで文字列"ping"を非同期通信で送信
ipcRenderer.send('asynchronous-message', 'ping');

//非同期通信の受信の応答処理(asynchronous-replyチャンネル)
ipcRenderer.on('asynchronous-reply', (event, arg) => {
  // "pong"が出力される
  console.log(arg);
});
main-procss.ts
// メインプロセスでやりとりするipcMain
const {ipcMain} = require('electron');
//asynchronous-messageチャンネルの受信処理
ipcMain.on('asynchronous-message', (event, arg) => {
  // "ping"が出力される
  console.log(arg);
  // event.senderに送信元のプロセスが設定されているので、asynchronous-replyチャンネルで文字列"pong"を非同期通信で送信元に送信
  event.sender.send('asynchronous-reply', 'pong');
  // ※event.senderはwebContentsオブジェクトな
});

同期通信

今度は同期通信やで。
送信元の関数がSendからSendSyncになってて、戻り値が取得できるで。

renderer-procss.ts
// レンダラープロセスでやりとりするipcRenderer
const {ipcRenderer} = require('electron');
// synchronous-messageチャンネルで文字列"ping"を同期通信で送信、戻り値を取得
var ret: string = ipcRenderer.sendSync('synchronous-message', 'ping');
// 戻り値"pong"が出力される
console.log(ret);
main-procss.ts
// メインプロセスでやりとりするipcMain
const {ipcMain} = require('electron');
//synchronous-messageチャンネルの受信処理
ipcMain.on('synchronous-message', (event, arg) => {
  // "ping"が出力される
  console.log(arg)
  // 呼び出し元の戻り値に文字列"pong"を設定
  event.returnValue = 'pong'
})

メインプロセス→レンダラープロセス

メインプロセスからはどのレンダラープロセスへ送信するか選択する必要があるってのはさっき書いたんやけど、送信する際にどのレンダラープロセスかを選択するんじゃなくて、送りたいプロセスのwebContentsオブジェクトを取得して、そこで関数呼び出しやからな。

main-process.ts
// BrowserWindow読み込み
const {BrowserWindow} = require('electron');
// indexとsubウィンドウ生成(コードは略)
var indexWindow: BrowserWindow = new BrowserWindow(・・・・・・);
var subWindow: BrowserWindow = new BrowserWindow(・・・・・・);

//indexウィンドウに対してい文字列"ping"を送信
indexWindow.webContents.send('asynchronous-message', 'ping');
//subウィンドウに対してい文字列"pong"を送信
subWindow.webContents.send('asynchronous-message', 'pong');
index-process.ts
// レンダラープロセスでやりとりするipcRenderer
const {ipcRenderer} = require('electron');
// asynchronous-messageチャンネルの受信処理
ipcRenderer.on('asynchronous-message', (msg) => {
  // アラートダイアログに"ping"が表示される  
  alert(msg);
});
sub-process.ts
// レンダラープロセスでやりとりするipcRenderer
const {ipcRenderer} = require('electron');
// asynchronous-messageチャンネルの受信処理
ipcRenderer.on('asynchronous-message', (msg) => {
  // アラートダイアログに"pong"が表示される  
  alert(msg);
});

プロセス間通信の使いどころ

実際のところ、メインとレンダラーでどのように処理をわけて、どうやって通信を使うかって話。
参考のページな。
https://electron.atom.io/docs/api/

  • 上の表でMainとあるAPIはメインプロセスで使う。
  • 上の表でRendererとあるAPIはレンダラープロセスで使う。
  • レンダラープロセスでイベントからメインプロセスへ通信して処理を返してもらう。
  • メインプロセスでレンダラープロセスを管理しておいて、メインプロセスで発生したイベントで対象のレンダラープロセスに通信する。

例えば・・・

  • 画面(レンダラープロセス)でボタンクリックしてメインプロセスに通信、処理結果を返してもらう。
  • メニュークリックイベント(メインプロセス)から画面(レンダラープロセス)に通信して処理を行う。

レンダラープロセス間の通信

上に書いたとおり、一旦メインプロセスを介して通信するで。

index-procss.ts
// レンダラープロセスでやりとりするipcRenderer
const {ipcRenderer} = require('electron');
// メイン→レンダラーは非同期通信なので非同期通信で行う
ipcRenderer.send('message', {win: 'sub', value: 'ping'});
main-procss.ts
// メインプロセスでやりとりするipcMain
const {ipcMain} = require('electron');
//synchronous-messageチャンネルの受信処理
ipcMain.on('message', (event, arg) => {
  if (arg.win === 'sub') {
    subWindow.webContents.send('message', arg.value);
  }
})
sub-procss.ts
// レンダラープロセスでやりとりするipcRenderer
const {ipcRenderer} = require('electron');
// 受信処理
ipcRenderer.on('message', (event, arg) => {
  // "ping"が出力される
  console.log(arg);
});

※注意

上に書いたソースコードでは、複数のファイルに同じモジュールの定義を書くと
Cannot redeclare block-scoped variable
というエラーが発生するさかい注意な。

こっちを参照。
https://trueman-developer.blogspot.jp/2017/05/nodejs-typescriptcannot-redeclare-block.html

次回

第5回 基本編-ウィンドウを作って表示

53
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
53
Help us understand the problem. What is going on with this article?