Electronアプリのバックグラウンド時動作を安定させるTips

[Electron](http://electron.atom.io/)で開発したアプリケーションを動作させていると、アプリケーションがバックグラウンド動作をしている時にフリーズしてしまうことがあります。これはバックグラウンド状態のアプリの動作優先度を下げられてしまうためで、その原因は特定のプラットフォーム特有の挙動による場合と、ElectronにバンドルされているChromiumの挙動による場合の２つが考えられます。

どちらの場合もElectronアプリ側から行える対処法がありますので、以下にそれぞれの対策をまとめます（おまけとしてChromiumの中でそれらがどのように処理されているかもリンクしておきました）。

# プラットフォーム側による動作優先度変更への対策
プラットフォームによってはOS Xの[AppNap](https://developer.apple.com/library/mac/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/AppNap.html#//apple_ref/doc/uid/TP40013929-CH2-SW1)のように、ユーザが操作中でないアプリの動作優先度を強制的に下げる機能を持っています。

このようなプラットフォーム側の機構を回避するためにElectronに用意されているのが[powerSaveBlocker](https://github.com/electron/electron/blob/master/docs/api/power-save-blocker.md)モジュールです。以下にpowerSaveBlockerモジュールの使い方を示します。

```js
import { powerSaveBlocker } from 'electron';

const id = powerSaveBlocker.start('prevent-app-suspension');

// 優先度を下げてほしくない処理

powerSaveBlocker.stop(id);
```

`powerSaveBlcoker#start`への引数は`prevent-app-suspension`と`prevent-display-sleep`の2種類がありますが、アプリの動作優先度を維持したいだけであれば`prevent-app-suspension`を利用します。startメソッドを呼び出してからstopを呼びだすまで、アプリの動作優先度が維持され、またコンピュータがスリープに入らなくなります。

powerSaveBlockerの処理はElectronではなく、ElectronにバンドルされているChromiumに実装されている機能で、各プラットフォームごとに別々の実装がされています。
例えば[OS X向けの実装](https://chromium.googlesource.com/chromium/src.git/+/master/content/browser/power_save_blocker_mac.cc)では[IOPMAssertionCreateWithName](https://developer.apple.com/library/mac/qa/qa1340/_index.html)というOS XのAPIを利用してMacがスリープ状態になるのを防いでいます。また、この処理で呼び出し元のアプリケーションがAppNapで低優先度動作になるのも防止されます。

ユーザが操作していないアプリケーションの優先度を下げたり、コンピュータが自動的にスリープ状態になるのは電力消費を抑えるための機能であり、本来は強制的に停止させるべきではありません。powerSaveBlockerを使う際はアプリケーションのライフサイクル全体で使うよりも、どうしても守りたい処理を行っている間だけに限るべきでしょう。

# Chromiumによる描画優先度変更への対策

ElectronにバンドルされているChromium自体も、現在表示中でないページの描画優先度を下げる機能があります。これにより、ウィンドウが隠れている際などに処理が極端に遅くなってアプリがフリーズしてしまうことがあります。

この機能を無効化するには以下のように[disable-renderer-backgroundingスイッチ](https://github.com/electron/electron/blob/master/docs/api/chrome-command-line-switches.md#--disable-renderer-backgrounding)を使用します。

```js
import { app } from 'electron';

app.commandLine.appendSwitch('disable-renderer-backgrounding');
app.on('ready', () => {
  ...
});
```

この処理は上記のように`ready`イベントが呼ばれる前に実行する必要がある点に注意してください。こちらも実際にはElectronではなく、Chromiumの動作に影響するのですが、[Chromiumのrendererプロセス処理](https://chromium.googlesource.com/chromium/src/+/1622a7bcfabc18669b4828cd510d47db784eb662/content/browser/renderer_host/render_process_host_impl.cc#2550)の中で表示状態に応じてバックグラウンド状態に設定されるのを防止することで描画の優先順位を維持します。

# 参考文献
* [powerSaveBlocker - GitHub](https://github.com/electron/electron/blob/master/docs/api/power-save-blocker.md)
* [Supported Chrome command line switches - GitHub](https://github.com/electron/electron/blob/master/docs/api/chrome-command-line-switches.md#--disable-renderer-backgrounding)
* [Process priority when application is minimized on dock or not focused  - GitHub Issues](https://github.com/electron/electron/issues/2822)
* [Seems like page-visibility option on BrowserWindow is broken - GitHub Issues](https://github.com/electron/electron/issues/3225)
* [Electron - Slow performances in background](https://pracucci.com/electron-slow-background-performances.html)
* [Extend App Nap - Energy Efficiency Guide for Mac Apps](https://developer.apple.com/library/mac/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/AppNap.html#//apple_ref/doc/uid/TP40013929-CH2-SW1)