在第一节中提到的startup函数里(srcvscodeelectron-mainmain.ts) 有一个createServices的调用:
代码语言:javascript复制const services = new ServiceCollection();
const environmentService = new EnvironmentService(args, process.execPath);
const instanceEnvironment = this.patchEnvironment(environmentService); // Patch `process.env` with the instance's environment
services.set(IEnvironmentService, environmentService);
const logService = new MultiplexLogService([new ConsoleLogMainService(getLogLevel(environmentService)), bufferLogService]);
process.once('exit', () => logService.dispose());
services.set(ILogService, logService);
services.set(IConfigurationService, new ConfigurationService(environmentService.settingsResource));
services.set(ILifecycleService, new SyncDescriptor(LifecycleService));
services.set(IStateService, new SyncDescriptor(StateService));
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService));
services.set(IThemeMainService, new SyncDescriptor(ThemeMainService));
services.set(ISignService, new SyncDescriptor(SignService));
return [new InstantiationService(services, true), instanceEnvironment];
在这个方法里,首先创建了一个ServiceCollection(srcvsplatforminstantiationcommonserviceCollection.ts) 这个ServiceCollection内部其实就是一个map对象;不用细说; 我们先看看这些service都是干嘛的
运行环境服务:EnvironmentService
路径:srcvsplatformenvironmentnodeenvironmentService.ts 这是一个工具类, 通过这个类可以获取程序的: 启动目录、日志目录、操作系统、配置文件目录、快捷键绑定配置路径.... 非常多!
多路日志服务:MultiplexLogService
路径:srcvsplatformlogcommonlog.ts 默认是用的控制台输出日志(ConsoleLogMainService) 也是一个工具类 包含trace(查看调用堆栈的),debug,info,warn,error,critical 还有dispose(释放日志服务,进程退出的时候回被调用)和setLevel(设置日志级别)的方法; 在同一个文件里,除了ConsoleLogMainService, 还实现了其他几种日志记录方式,不多做介绍了;
配置服务:ConfigurationService
路径:srcvsplatformconfigurationnodeconfigurationService.ts 从运行环境服务(environmentService)里,拿到配置文件的路径 读出配置文件的内容,然后提供配置项的读写功能; 配置项变更的时候,会有相应的事件触发出来;
生命周期服务:LifecycleService
路径:srcvsplatformlifecycleelectron-mainlifecycleMain.ts 在这里监听了一系列的electron的事件 比如: before-quit、window-all-closed、will-quit等 事件被触发的时候,做了下面一些事情 记日志、屏蔽electron默认的处理逻辑、执行自己的逻辑
状态服务:StateService
路径:srcvsplatformstatenodestateService.ts 在storage.json里记录一些与程序运行状态有关的键值对(也可以删除)
请求服务:RequestService
路径:srcvsplatformrequestelectron-mainrequestService.ts 使用electron提供的net.request方法,发起请求(支持代理和SSL)
诊断服务:DiagnosticsService
路径:srcvsplatformdiagnosticselectron-maindiagnosticsService.ts 根据不同的操作系统,计算CPU消耗、内存消耗、GPU消耗等
界面主题服务:ThemeMainService
路径:srcvsplatformthemeelectron-mainthemeMainService.ts 获取背景色、设置背景色 数据通过stateService保存
程序签名服务:SignService
路径:srcvsplatformlifecycleelectron-mainlifecycleMain.ts 这个服务为程序的签名提供帮助 缓存了一个vsda的import,目的是为了解决签名时的一个BUG
实例化服务:InstantiationService
这个服务比较特殊,不是在本文一开始所讲的代码里设置的 前面的代码中有这么一行:
代码语言:javascript复制return [new InstantiationService(services, true), instanceEnvironment];
这个服务就是在它自身的内部保存到ServiceCollection
代码语言:javascript复制 constructor(services: ServiceCollection = new ServiceCollection(), strict: boolean = false, parent?: InstantiationService) {
this._services = services;
this._strict = strict;
this._parent = parent;
this._services.set(IInstantiationService, this);
}
这个服务提供了反射、实例化的一些方法; 用于创建具体的类型的实例
服务的初始化工作
服务的对象创建出来之后,有些服务需要完成初始化才能使用 这是在main.ts的initServices中完成的(srcvscodeelectron-mainmain.ts)
代码语言:javascript复制 // Environment service (paths)
const environmentServiceInitialization = Promise.all<void | undefined>([
environmentService.extensionsPath,
environmentService.nodeCachedDataDir,
environmentService.logsPath,
environmentService.globalStorageHome,
environmentService.workspaceStorageHome,
environmentService.backupHome
].map((path): undefined | Promise<void> => path ? mkdirp(path) : undefined));
// Configuration service
const configurationServiceInitialization = configurationService.initialize();
// State service
const stateServiceInitialization = stateService.init();
return Promise.all([environmentServiceInitialization, configurationServiceInitialization, stateServiceInitialization]);
可以看到这个方法里创建了一大堆目录; 创建目录的方法是:(srcvsbasenodepfs.ts)
代码语言:javascript复制const mkdir = async () => {
try {
await promisify(fs.mkdir)(path, mode);
} catch (error) {
// ENOENT: a parent folder does not exist yet
if (error.code === 'ENOENT') {
return Promise.reject(error);
}
// Any other error: check if folder exists and
// return normally in that case if its a folder
try {
const fileStat = await stat(path);
if (!fileStat.isDirectory()) {
return Promise.reject(new Error(`'${path}' exists and is not a directory.`));
}
} catch (statError) {
throw error; // rethrow original error
}
}
};
另外: 最后几个服务的创建(严格说还没有创建)都用到了SyncDescriptor(srcvsplatforminstantiationcommondescriptors.ts) 这里我们解释一下SyncDescriptor,是个简单的泛型类型; 一个它的实例,可以持有一个类型(传入构造函数的类型),这个类型可以等到用的时候再实例化;