Electron 进程保护避免崩掉,在某种情况下,我们可能希望我们的客户端程序尽可能连续不断的运行在我们的系统中,并保持稳定。

当渲染进程崩溃或被结束时触发


此事件是用来家庭渲染进程崩溃的,但是当主进程意外崩溃时也会触发该事件。


在监测到程序崩溃后,我们要让程序重新启动,此时我们要首先判断window对象是否被销毁,也就是主进程是否被杀死,还是渲染进程崩溃,同时作出不同的处理。

————————————————


在某种情况下,我们可能希望我们的客户端程序尽可能连续不断的运行在我们的系统中,并保持稳定。

以下几种方式可以帮助我们做到这一点:

1.崩溃监控

electron为我们提供了监听程序崩溃的事件:

  1.  

    Event: 'crashed'

  2.  

    返回:

  3.  

     

  4.  

    event Event

  5.  

    killed Boolean

  6.  

    当渲染进程崩溃或被结束时触发

此事件是用来家庭渲染进程崩溃的,但是当主进程意外崩溃时也会触发该事件。

在监测到程序崩溃后,我们要让程序重新启动,此时我们要首先判断window对象是否被销毁,也就是主进程是否被杀死,还是渲染进程崩溃,同时作出不同的处理。

当mainWin被销毁时我们直接重启整个应用,使用如下api:

  1.  

    app.relaunch([options])

  2.  

    options Object (可选)

  3.  

     

  4.  

    args String

  5.  

    execPath String (可选)

  6.  

    从当前实例退出,重启应用。

  7.  

     

  8.  

    默认情况下,新的实例会和当前实例使用相同的工作目录以及命令行参数。 当设置了 args 参数时, args 将作为命令行参数传递。 当设置了 execPath ,execPath 将被执行以重新启动,而不是当前的应用程序。

  9.  

     

  10.  

    请注意, 此方法在执行时不会退出当前的应用程序, 你需要在调用 app.relaunch 方法后再执行 app. quit 或者 app.exit 来让应用重启。

只是渲染进程崩溃,我们只需将其他窗体销毁,然后重新load我们的主窗口。

崩溃重启逻辑:

  1.  

    if (mainWin.isDestroyed()) {

  2.  

    app.relaunch();

  3.  

    app.exit(0);

  4.  

    else {

  5.  

    BrowserWindow.getAllWindows().forEach((w) => {

  6.  

    if (w.id !== mainWin.id) w.destroy();

  7.  

    });

  8.  

    mainWin.reload();

  9.  

    }

当然,我们还要记录一下程序的崩溃日志,我们要确保日志接口发出成功后再重启我们的程序:

下面是程序崩溃后的完整逻辑:

  1.  

    import { BrowserWindow, app, dialog} from 'electron';

  2.  

     

  3.  

     

  4.  

    const mainWindow = BrowserWindow.fromId(global.mainId);

  5.  

    mainWindow.webContents.on('crashed', () => {

  6.  

    const options = {

  7.  

    type: 'error',

  8.  

    title: '进程崩溃了',

  9.  

    message: '这个进程已经崩溃.',

  10.  

    buttons: ['重载', '退出'],

  11.  

    };

  12.  

    recordCrash().then(() => {

  13.  

    dialog.showMessageBox(options, (index) => {

  14.  

    if (index === 0) reloadWindow(mainWindow);

  15.  

    else app.quit();

  16.  

    });

  17.  

    }).catch((e) => {

  18.  

    console.log('err', e);

  19.  

    });

  20.  

    })

  21.  

     

  22.  

    function recordCrash() {

  23.  

    return new Promise(resolve => {

  24.  

    // 崩溃日志请求成功....

  25.  

    resolve();

  26.  

    })

  27.  

    }

  28.  

     

  29.  

    function reloadWindow(mainWin) {

  30.  

    if (mainWin.isDestroyed()) {

  31.  

    app.relaunch();

  32.  

    app.exit(0);

  33.  

    else {

  34.  

    BrowserWindow.getAllWindows().forEach((w) => {

  35.  

    if (w.id !== mainWin.id) w.destroy();

  36.  

    });

  37.  

    mainWin.reload();

  38.  

    }

  39.  

    }

写好代码之后,我们可以直接在控制台输入 process.crash()来进行测试,或者直接在任务管理器杀掉我们的进程进行测试。

2.开机自启

开机自启是保证我们的程序能长时间在机器上运行很重要的一点。

电脑上有很多程序都设置了开机自启动,比如qq,微信,迅雷等,他们都是通过修改注册表来实现的,我们可以看一下注册表\Software\Microsoft\Windows\CurrentVersion\Run:

image

所以我们也要将我们程序的路径写到这里。

发现了一个非常好的写注册表的模块,winreg
注意mac不能使用这个模块,所以首先要判断是否为window再引用这个模块。

借助这个模块我们可以非常简单的修改注册表:

  1.  

    const WinReg = require('winreg');

  2.  

     

  3.  

    const startOnBoot = {

  4.  

    enableAutoStart: function (name, file, callback) {

  5.  

    var key = getKey();

  6.  

    key.set(name, WinReg.REG_SZ, file, callback || noop);

  7.  

    },

  8.  

    disableAutoStart: function (name, callback) {

  9.  

    var key = getKey();

  10.  

    key.remove(name, callback || noop);

  11.  

    },

  12.  

    getAutoStartValue: function (name, callback) {

  13.  

    var key = getKey();

  14.  

    key.get(name, function (error, result) {

  15.  

    if (result) {

  16.  

    callback(result.value);

  17.  

    }

  18.  

    else {

  19.  

    callback(null, error);

  20.  

    }

  21.  

    });

  22.  

    }

  23.  

    };

  24.  

     

  25.  

    function noop() { }

  26.  

     

  27.  

    const RUN_LOCATION = '\Software\Microsoft\Windows\CurrentVersion\Run';

  28.  

    function getKey() {

  29.  

    return new WinReg({

  30.  

    hive: WinReg.HKCU, //CurrentUser,

  31.  

    key: RUN_LOCATION

  32.  

    });

  33.  

    }

  34.  

     

  35.  

    export default function autoStart() {

  36.  

    startOnBoot.getAutoStartValue('MY_CLIENT_AUTOSTART', function (value) {

  37.  

    if (!value) {

  38.  

    startOnBoot.enableAutoStart('MY_CLIENT_AUTOSTART', process.execPath, function () { console.log('开机自动启设置'); });

  39.  

    }

  40.  

    });

  41.  

    }

执行完程序之后,再看注册表,发现我们程序的路径已经写进去了:

image

然后电脑重启后你的程序就自动启动了。

3.托盘关闭

向qq和微信一样,有的时候我们并不想让用户通过点关闭按钮的时候就关闭程序,而是把程序最小化到托盘,在托盘上做真正的退出操作。

首先要监听窗口的关闭事件,阻止用户关闭操作的默认行为。

  1.  

    mainWindow.on('close', (event) => {

  2.  

    mainWindow.hide();

  3.  

    event.preventDefault();

  4.  

    });

然而这时你发现,这只是最小化了程序,任务栏里程序依然存在,我们需要让程序在任务栏里也消失:

  1.  

    mainWindow.on('close', (event) => {

  2.  

    mainWindow.hide();

  3.  

    mainWindow.setSkipTaskbar(true);

  4.  

    event.preventDefault();

  5.  

    });

这时程序就再也找不到了,任务托盘中也没有我们的程序,所以我们要先创建好任务托盘,并做好事件监听:

  1.  

    function createTray() {

  2.  

    const mainWindow = BrowserWindow.fromId(global.mainId);

  3.  

    tray = new Tray(path.join(global.__dirname, 'icon.ico'));

  4.  

    const contextMenu = Menu.buildFromTemplate([

  5.  

    { label: '退出', click: () => { mainWindow.destroy(); app.quit(); } },

  6.  

    ])

  7.  

    tray.setToolTip('我的客户端')

  8.  

    tray.setContextMenu(contextMenu)

  9.  

    tray.on('click', () => {

  10.  

    if (mainWindow.isVisible()) {

  11.  

    mainWindow.hide();

  12.  

    mainWindow.setSkipTaskbar(false);

  13.  

    else {

  14.  

    mainWindow.show();

  15.  

    mainWindow.setSkipTaskbar(true);

  16.  

    }

  17.  

    })

  18.  

    }

以上这些操作为我们的程序又增加了好几层的防护措施,我们的程序就不会那么轻而易举的挂掉啦!