# 插件生命周期 本文档详细介绍阑山桌面插件的生命周期、加载流程和各个阶段的职责。 ## 生命周期概览 插件从加载到卸载经历以下阶段: ``` ┌─────────────────────────────────────────────────────────┐ │ 插件生命周期 │ └─────────────────────────────────────────────────────────┘ 1. 发现 (Discovery) ├─ 扫描插件目录 ├─ 读取 plugin.json └─ 验证基本信息 ↓ 2. 加载 (Load) ├─ 创建 PluginLoadContext ├─ 加载程序集 ├─ 解析依赖关系 └─ 验证兼容性 ↓ 3. 实例化 (Instantiate) ├─ 反射查找 IPlugin 实现 ├─ 创建插件实例 └─ 注入依赖 ↓ 4. 初始化 (Initialize) ├─ 调用 InitializeAsync() ├─ 注册组件 ├─ 注册设置页 ├─ 注册服务 └─ 订阅事件 ↓ 5. 运行中 (Running) ├─ 组件渲染和更新 ├─ 处理用户交互 ├─ 响应事件 └─ 执行后台任务 ↓ 6. 关闭 (Shutdown) ├─ 调用 ShutdownAsync() ├─ 保存状态 ├─ 取消订阅 ├─ 清理资源 └─ 卸载程序集 ``` ## 详细阶段说明 ### 1. 发现阶段 (Discovery) **时机**: 宿主启动时 **职责**: 扫描和识别插件 **流程**: ```csharp // PluginDiscoveryService.cs (宿主代码) public List DiscoverPlugins() { var pluginsDir = Path.Combine( AppDataPath, "plugins" ); var descriptors = new List(); // 1. 扫描插件目录 foreach (var dir in Directory.GetDirectories(pluginsDir)) { var manifestPath = Path.Combine(dir, "plugin.json"); // 2. 读取 plugin.json if (!File.Exists(manifestPath)) { _logger.LogWarning($"Plugin manifest not found: {dir}"); continue; } try { var json = File.ReadAllText(manifestPath); var manifest = JsonSerializer.Deserialize(json); // 3. 验证基本信息 if (string.IsNullOrEmpty(manifest?.Id)) { _logger.LogWarning($"Invalid plugin manifest: {dir}"); continue; } descriptors.Add(new PluginDescriptor { Id = manifest.Id, Name = manifest.Name, Version = manifest.Version, Directory = dir, Manifest = manifest }); } catch (Exception ex) { _logger.LogError(ex, $"Failed to read plugin manifest: {dir}"); } } return descriptors; } ``` **开发者注意事项**: - ✅ 确保 `plugin.json` 存在且格式正确 - ✅ 确保插件 ID 唯一 - ✅ 确保版本号符合语义化版本规范 ### 2. 加载阶段 (Load) **时机**: 发现插件后 **职责**: 加载插件程序集 **流程**: ```csharp // PluginLoader.cs (宿主代码) public PluginLoadResult LoadPlugin(PluginDescriptor descriptor) { try { // 1. 创建隔离的加载上下文 var loadContext = new PluginLoadContext(descriptor.Directory); // 2. 查找主程序集 var assemblyPath = Path.Combine( descriptor.Directory, $"{descriptor.Id}.dll" ); if (!File.Exists(assemblyPath)) { return PluginLoadResult.Failed( $"Plugin assembly not found: {assemblyPath}" ); } // 3. 加载程序集 var assembly = loadContext.LoadFromAssemblyPath(assemblyPath); // 4. 验证依赖 if (!ValidateDependencies(descriptor.Manifest.Dependencies)) { return PluginLoadResult.Failed("Dependency validation failed"); } // 5. 验证宿主版本兼容性 if (!IsHostVersionCompatible(descriptor.Manifest.MinHostVersion)) { return PluginLoadResult.Failed( $"Incompatible host version. Required: {descriptor.Manifest.MinHostVersion}" ); } return PluginLoadResult.Success(assembly, loadContext); } catch (Exception ex) { _logger.LogError(ex, $"Failed to load plugin: {descriptor.Id}"); return PluginLoadResult.Failed(ex.Message); } } ``` **依赖解析**: ```json // plugin.json { "Dependencies": [ { "PluginId": "com.example.anotherplugin", "MinVersion": "1.0.0" } ] } ``` **开发者注意事项**: - ✅ 主程序集名称应与插件 ID 匹配(或在清单中指定) - ✅ 所有依赖的 DLL 应在插件目录中 - ✅ 声明对其他插件的依赖关系 ### 3. 实例化阶段 (Instantiate) **时机**: 程序集加载后 **职责**: 创建插件实例 **流程**: ```csharp // PluginActivator.cs (宿主代码) public IPlugin? CreatePluginInstance(Assembly assembly) { try { // 1. 查找 IPlugin 实现类 var pluginType = assembly.GetTypes() .FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface ); if (pluginType == null) { _logger.LogError("No IPlugin implementation found"); return null; } // 2. 创建实例 var plugin = Activator.CreateInstance(pluginType) as IPlugin; if (plugin == null) { _logger.LogError("Failed to create plugin instance"); return null; } return plugin; } catch (Exception ex) { _logger.LogError(ex, "Failed to instantiate plugin"); return null; } } ``` **开发者注意事项**: - ✅ 插件类必须有公共无参构造函数 - ✅ 一个插件程序集只能有一个 IPlugin 实现 - ✅ 不要在构造函数中执行耗时操作 ### 4. 初始化阶段 (Initialize) **时机**: 插件实例创建后 **职责**: 注册组件、服务和事件 **插件代码示例**: ```csharp public class MyPlugin : IPlugin { public async Task InitializeAsync(IPluginContext context) { // 1. 记录日志 context.Logger.LogInformation($"{Name} is initializing..."); // 2. 注册组件 var componentRegistry = context.Services .GetService(); if (componentRegistry != null) { // 注册多个组件 componentRegistry.RegisterComponent(); componentRegistry.RegisterComponent(); context.Logger.LogInformation("Components registered"); } // 3. 注册设置页 var settingsRegistry = context.Services .GetService(); if (settingsRegistry != null) { settingsRegistry.RegisterPage( title: "我的插件", category: "插件" ); context.Logger.LogInformation("Settings page registered"); } // 4. 注册公共 IPC 服务(如果需要) var ipcBuilder = context.Services .GetService(); if (ipcBuilder != null) { ipcBuilder.AddService( objectId: "default", notifyIds: new[] { "myplugin.event.changed" } ); } // 5. 订阅宿主事件 var eventBus = context.Services .GetService(); if (eventBus != null) { eventBus.Subscribe(OnThemeChanged); } // 6. 初始化后台服务(如果有) await InitializeBackgroundServicesAsync(context); context.Logger.LogInformation($"{Name} initialized successfully"); } private void OnThemeChanged(ThemeChangedEvent evt) { // 响应主题变更 } private async Task InitializeBackgroundServicesAsync(IPluginContext context) { // 启动定时任务等 await Task.CompletedTask; } } ``` **初始化最佳实践**: ```csharp public async Task InitializeAsync(IPluginContext context) { try { // ✅ 使用 try-catch 捕获异常 // ✅ 记录详细的日志 // ✅ 验证服务是否可用 // ✅ 使用 async/await 处理异步操作 // ❌ 不要阻塞 UI 线程 // ❌ 不要执行超过 5 秒的操作 _context = context; // 快速初始化 RegisterComponents(context); RegisterSettings(context); // 耗时操作使用后台任务 _ = Task.Run(async () => { await LoadDataAsync(); }); } catch (Exception ex) { context.Logger.LogError(ex, "Plugin initialization failed"); throw; // 让宿主知道初始化失败 } } ``` **开发者注意事项**: - ✅ InitializeAsync 应尽快完成(< 5 秒) - ✅ 耗时操作放在后台线程 - ✅ 妥善处理异常 - ✅ 保存 IPluginContext 引用供后续使用 - ❌ 不要在此阶段访问其他插件的服务(可能还未加载) ### 5. 运行中阶段 (Running) **时机**: 初始化完成后 **职责**: 响应用户交互和系统事件 **组件更新循环**: ```csharp // 宿主会定时调用组件的 UpdateAsync() public class MyComponent : ComponentBase { private HttpClient _httpClient; private DateTime _lastUpdate; public override async Task UpdateAsync() { // 定时更新数据(默认 1 秒调用一次) if (DateTime.Now - _lastUpdate > TimeSpan.FromMinutes(5)) { await FetchDataAsync(); _lastUpdate = DateTime.Now; } } private async Task FetchDataAsync() { try { var data = await _httpClient.GetStringAsync("https://api.example.com/data"); // 更新组件属性 Data = ParseData(data); } catch (Exception ex) { Logger.LogError(ex, "Failed to fetch data"); } } } ``` **事件响应**: ```csharp public class MyPlugin : IPlugin { public async Task InitializeAsync(IPluginContext context) { // 订阅系统事件 var eventBus = context.Services.GetService(); eventBus?.Subscribe(OnThemeChanged); eventBus?.Subscribe(OnLanguageChanged); eventBus?.Subscribe(OnSettingChanged); } private void OnThemeChanged(ThemeChangedEvent evt) { _logger.LogInformation($"Theme changed to: {evt.NewTheme}"); // 更新组件外观 } private void OnLanguageChanged(LanguageChangedEvent evt) { _logger.LogInformation($"Language changed to: {evt.NewLanguage}"); // 重新加载本地化资源 } private void OnSettingChanged(SettingChangedEvent evt) { if (evt.Key.StartsWith("MyPlugin.")) { // 响应插件设置变更 } } } ``` **开发者注意事项**: - ✅ 组件更新应快速完成 - ✅ 使用缓存避免重复计算 - ✅ 异步操作使用 async/await - ✅ 妥善处理网络错误 - ❌ 不要在 UpdateAsync 中执行超过 1 秒的操作 ### 6. 关闭阶段 (Shutdown) **时机**: - 宿主应用退出 - 插件被禁用 - 插件热重载 **职责**: 清理资源和保存状态 **插件代码示例**: ```csharp public class MyPlugin : IPlugin { private IDisposable? _eventSubscription; private HttpClient? _httpClient; private CancellationTokenSource? _cts; public async Task ShutdownAsync() { try { _logger.LogInformation($"{Name} is shutting down..."); // 1. 取消正在进行的操作 _cts?.Cancel(); // 2. 取消事件订阅 _eventSubscription?.Dispose(); // 3. 保存状态 await SaveStateAsync(); // 4. 释放资源 _httpClient?.Dispose(); // 5. 停止后台任务 await StopBackgroundServicesAsync(); _logger.LogInformation($"{Name} shutdown completed"); } catch (Exception ex) { _logger.LogError(ex, "Error during plugin shutdown"); // 不要抛出异常,避免影响其他插件 } } private async Task SaveStateAsync() { // 保存插件状态到设置 _context?.Settings.SetValue("LastUpdateTime", DateTime.Now); await Task.CompletedTask; } private async Task StopBackgroundServicesAsync() { // 停止定时任务等 await Task.CompletedTask; } } ``` **关闭最佳实践**: ```csharp public async Task ShutdownAsync() { try { // ✅ 尽快完成(< 3 秒) // ✅ 使用 try-catch 避免异常 // ✅ 按相反顺序清理资源 // ✅ 保存关键状态 // ❌ 不要抛出异常 // ❌ 不要执行耗时操作 // 取消异步操作 _cancellationTokenSource?.Cancel(); // 取消订阅(防止内存泄漏) UnsubscribeEvents(); // 释放托管资源 DisposeResources(); // 保存状态(快速) SaveCriticalState(); } catch (Exception ex) { // 记录但不抛出 _logger?.LogError(ex, "Shutdown error"); } } ``` **开发者注意事项**: - ✅ ShutdownAsync 必须快速完成(< 3 秒) - ✅ 取消所有异步操作 - ✅ 取消事件订阅(防止内存泄漏) - ✅ 释放所有 IDisposable 资源 - ✅ 保存关键状态 - ❌ 不要抛出异常 ## 生命周期事件 插件可以监听宿主的生命周期事件: ```csharp public class MyPlugin : IPlugin { public async Task InitializeAsync(IPluginContext context) { var hostLifecycle = context.Services .GetService(); if (hostLifecycle != null) { hostLifecycle.Starting += OnHostStarting; hostLifecycle.Started += OnHostStarted; hostLifecycle.Stopping += OnHostStopping; hostLifecycle.Stopped += OnHostStopped; } } private void OnHostStarting(object? sender, EventArgs e) { // 宿主正在启动 } private void OnHostStarted(object? sender, EventArgs e) { // 宿主已启动完成 } private void OnHostStopping(object? sender, EventArgs e) { // 宿主即将关闭 } private void OnHostStopped(object? sender, EventArgs e) { // 宿主已关闭 } public async Task ShutdownAsync() { // 取消订阅 var hostLifecycle = _context?.Services .GetService(); if (hostLifecycle != null) { hostLifecycle.Starting -= OnHostStarting; hostLifecycle.Started -= OnHostStarted; hostLifecycle.Stopping -= OnHostStopping; hostLifecycle.Stopped -= OnHostStopped; } } } ``` ## 错误处理 ### 初始化失败 如果插件初始化失败,宿主会: 1. 记录错误日志 2. 标记插件为"加载失败" 3. 继续加载其他插件 4. 在 UI 中显示失败状态 ### 运行时异常 组件代码中的未捕获异常: 1. 被宿主捕获并记录 2. 组件标记为"错误"状态 3. 组件停止更新 4. 不影响其他组件 ### 关闭超时 如果 ShutdownAsync 超过 5 秒: 1. 宿主强制终止 2. 记录超时警告 3. 继续关闭其他插件 ## 插件热重载 宿主支持插件热重载(开发中功能): ``` 1. 用户触发重载 ↓ 2. 调用 ShutdownAsync() ↓ 3. 卸载程序集 ↓ 4. 重新加载程序集 ↓ 5. 创建新实例 ↓ 6. 调用 InitializeAsync() ↓ 7. 恢复组件状态 ``` ## 小结 插件生命周期的关键点: - ✅ **发现**: 确保 plugin.json 正确 - ✅ **加载**: 管理好依赖关系 - ✅ **初始化**: 快速注册,耗时操作后台执行 - ✅ **运行**: 高效更新,异步处理 - ✅ **关闭**: 及时清理,避免异常 ## 下一步 - [组件系统详解](02-组件系统.md) - 学习组件开发 - [设置系统](03-设置系统.md) - 管理插件配置 - [插件通信](05-插件通信.md) - 插件间协作 - [IPlugin 接口](../03-API参考/01-IPlugin接口.md) - API 详细文档