在上一篇 Electron 进程通信 中,介绍了 Electron 中的两种进程通信方式,分别为:

ipcMainipcRenderer
remoteremote
remote
通过 remote 对象,我们可以不必发送进程间消息来进行通信。但实际上,我们在调用远程对象的方法、函数或者通过远程构造函数创建一个新的对象,实际上都是在发送一个同步的进程间消息(官方文档 上说这类似于 JAVA 中的 RMI)。
也就是说,remote 方法只是不用让我们显式的写发送进程间的消息的方法而已。在上面通过 remote 模块创建 BrowserWindow的例子里。我们在渲染进程中创建的 BrowserWindow对象其实并不在我们的渲染进程中,它只是让主进程创建了一个 BrowserWindow对象,并返回了这个相对应的远程对象给了渲染进程。

但是只是这样吗?

remote

"假" 的多进程?

remote
remote

逻辑比较简单,直接看代码。

使用 IPC 模块

主进程代码:

渲染进程代码:

index.html
index.js

界面输出结果如下:



嗯..没什么问题,和预期一样。由于进程通信中数据传递经过了序列化和反序列化,渲染进程拿到的进程中的对象已经不是同一个对象,指向的内存地址不同。

remote

主进程代码:

渲染进程代码:

index.html
index.jsremote

界面输出结果如下:





remoteremoteObjremote

Java's RMI

remote
remote

RMI (Remote Method Invoke)

远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制。使用这种机制,某一台计算机上的对象可以调用另外一台计算机上的对象来获取远程数据。

如果使用 http 协议来实现远程方法调用,我们可能会这么实现:



remote

但是 IPC 通信是可以做到对用户来说是隐藏的。RMI 的目的也一样,要实现客户端像调用本地方法一样调用远程对象上的方法,底层的通信不应该暴露给用户。

RMI 实现原理

JRMP (Java Remote Method Protocol)



与 http 类似,但是这里多了个注册表。

这里的注册表可以类比于我们的 DNS 服务器。



rmi://localhost:8000/hellormi://localhost:8000/hello

数据传递

注册表返回对象 A 是怎么传递给客户端的呢?首先想到的自然是序列化 & 反序列化。 RMI 也是这么实现的,不过分了几种情况:

java.rmi.Remote
Remote

RMI 的大致流程



比较懵逼?没关系,看代码实现:

RMI 简单实现

(建议大家一起运行下这个例子~不动手实现怎么会有成就感!!)

HelloRMI.java
HelloRMIHelloImpl.java
Server.java
Client.java
Server.java
remote
remote



remoteremote



remoteRemoteRemoteRemoteremoteObject
remote
lib/renderer/api/remote.js
remote
getBuiltinmetametaToValue
metaToValue
Object

对返回对象属性重写 get、set 方法。对调用远程对象上的属性,同样是通过发送同步的进程间消息来获取,这也就是为什么主进程修改了值,渲染进程就也能感知到的原因了。

还有一个需要注意的地方是,为了不重复获取远程对象,对返回的对象 remote 是会进行缓存的,看 metaToValue 的倒数第二行:remoteObjectCache.set(meta.id, ret)

读者思考

到这里我们知道了文章开头遇到的神奇现象的原因。这里抛出个问题给读者:思考下如果是主进程的函数是异步的(函数返回一个 Promise 对象),Promise 对象是如何实现数据传递的?是否会阻塞渲染进程?

总结

remote

【参考资料】