2.4 主进程Node.js环境
在用户启动Electron应用后,首先执行的是整个应用的入口函数wWinMain,该函数在shell\app\electron_main.cc文件内实现,wWinMain是Windows系统下的入口函数,其他系统入口方法略有不同。
Electron在入口函数内完成了很多工作,比如处理命令行指令、初始化环境变量等,但我们关注的只有下面这几行代码:
electron::ElectronMainDelegate delegate; content::ContentMainParams params(&delegate); params.instance = instance; params.sandbox_info = &sandbox_info; electron::ElectronCommandLine::Init(arguments.argc, arguments.argv); return content::ContentMain(params);
以上代码中的ContentMain方法是Chromium项目中定义的方法,这个方法负责启动Chromium,它接收一个参数,这个参数持有一个代理对象(shell\app\electron_main_delegate.cc),对Chromium进行二次开发,工程师可以利用这个代理对象完成一些自定义的初始化工作。Electron的工程师就是使用了这个参数,为Chromium的启动过程注入了自己的逻辑。下面就来介绍Electron是如何利用这个代理对象注入自己的逻辑的。
首先Electron在启动Chromium之初,创建了一个ElectronBrowserClient对象(shell\browser\electron_browser_client.cc),这个对象继承自Chromium的ContentBrowserClient类,ContentBrowserClient类提供了一系列的虚方法,比如IsBrowserStartupComplete(判断浏览器核心是否完全启动)、IsShuttingDown(判断浏览器核心是否被关闭)、RenderProcess-WillLaunch(是否有新的渲染进程将要被加载)等,ElectronBrowserClient类继承自Content-BrowserClient类,同时也拥有了这个类的能力。
接下来Electron通过ElectronBrowserClient类的CreateBrowserMainParts方法创建了一个ElectronBrowserMainParts对象(shell\browser\electron_browser_main_parts.cc),这个对象继承自Chromium的BrowserMainParts对象,包含Chromium启动过程中的一系列“事件”(这里为了便于理解,写作“事件”,实际上并不是事件),Electron就是在这个对象的PostEarlyInitialization事件中初始化了Node.js环境。
PostEarlyInitialization事件是Chromium启动的早期事件,类似的还有PreCreateMain-MessageLoop(浏览器主进程消息循环开始前事件)、PostCreateMainMessageLoop(浏览器主进程消息循环开始后事件)、OnFirstIdle(主进程第一次进入空闲时的事件)等。
Electron在PostEarlyInitialization事件执行时初始化Node.js的运行环境,其关键代码如下所示:
js_env_ = std::make_unique<JavascriptEnvironment>(node_bindings_->uv_loop()); v8::HandleScope scope(js_env_->isolate()); node_bindings_->Initialize(); node::Environment* env = node_bindings_->CreateEnvironment( js_env_->context(), js_env_->platform()); node_env_ = std::make_unique<NodeEnvironment>(env); env->set_trace_sync_io(env->options()->trace_sync_io); electron_bindings_->BindTo(js_env_->isolate(), env->process_object()); node_bindings_->LoadEnvironment(env); node_bindings_->set_uv_env(env);
上述代码中,electron_bindings_是ElectronBindings类的一个实例,其BindTo方法为process对象提供了一系列的扩展方法和属性,比如process对象的getCPUUsage、crash、getCreationTime等方法。
node_bindings是NodeBindings类的实例,其Initialize方法初始化了Electron的操作系统支持逻辑,比如剪贴板、系统菜单、托盘图标等的控制逻辑,这部分内容还会在后面进一步讲解。
node_bindings对象的LoadEnvironment方法最终初始化Node.js的运行环境,至此Electron的主进程就具备了Node.js的执行环境,接下来就可以加载并执行用户的入口脚本文件了。