|
|
|
@ -2,6 +2,144 @@ |
|
|
|
|
|
|
|
## 2025年修改记录 |
|
|
|
|
|
|
|
### ImageGalleryPageViewModel 优化大量图片时的恢复策略 - 避免7000+图片时的性能问题 |
|
|
|
- **日期**: 2025年1月 |
|
|
|
- **修改内容**: 优化视图重新激活时的缩略图恢复策略,避免在有大量图片(7000+)时触发大量异步操作 |
|
|
|
- **问题分析**: |
|
|
|
- ❌ **大量异步操作**: 当有7000+个图片时,如果对所有图片都立即恢复,会触发大量异步操作,导致性能问题 |
|
|
|
- ❌ **不必要的恢复**: 不可见的图片不需要立即恢复,应该在滚动到它们时按需加载 |
|
|
|
- ❌ **性能瓶颈**: 即使分批恢复,7000个图片意味着70批,仍然会产生大量延迟 |
|
|
|
- **修改文件**: |
|
|
|
- `AuroraDesk.Presentation/ViewModels/Pages/ImageGalleryPageViewModel.cs` (优化恢复策略,只恢复可见区域附近的图片) |
|
|
|
- `AuroraDesk.Presentation/Views/Pages/ImageGalleryPageView.axaml.cs` (使用优化后的恢复方法) |
|
|
|
- **主要优化**: |
|
|
|
- ✅ **只恢复可见区域**: `ResetAllThumbnailReferences` 现在只恢复可见区域附近的图片(前后各扩展100个),而不是所有图片 |
|
|
|
- ✅ **快速重置标志位**: 先快速重置所有标志位(O(n)操作),然后只恢复可见区域附近的图片 |
|
|
|
- ✅ **按需加载机制**: 其他图片在 UI 绑定时通过 `EnsureThumbnailLoaded` 按需加载,不会影响性能 |
|
|
|
- ✅ **智能恢复**: `EnsureThumbnailLoaded` 现在会检查标志位,如果被清理且 `_thumbnailSource` 为 null,从缓存恢复 |
|
|
|
- ✅ **添加标志位重置方法**: 添加 `ResetThumbnailReferenceClearedFlagOnly()` 方法,只重置标志位,不触发恢复 |
|
|
|
- **技术细节**: |
|
|
|
- `ResetAllThumbnailReferences` 接受可见区域索引参数,如果提供则只恢复可见区域附近的图片 |
|
|
|
- 如果没有提供可见区域信息,只恢复前100个(保守策略) |
|
|
|
- 先对所有图片调用 `ResetThumbnailReferenceClearedFlagOnly()` 快速重置标志位 |
|
|
|
- 然后只对可见区域附近的图片调用 `ResetThumbnailReferenceCleared()` 触发恢复 |
|
|
|
- 其他图片在 UI 绑定时通过 `EnsureThumbnailLoaded` 按需加载 |
|
|
|
- `EnsureThumbnailLoaded` 检查标志位,如果被清理且 `_thumbnailSource` 为 null,从缓存恢复 |
|
|
|
- **效果**: |
|
|
|
- ✅ **性能大幅提升**: 即使有7000+个图片,也只恢复可见区域附近的图片(约200-300个),而不是所有7000个 |
|
|
|
- ✅ **UI 流畅**: 视图重新激活时立即响应,不会因为大量异步操作导致卡顿 |
|
|
|
- ✅ **按需加载**: 其他图片在滚动到它们时按需加载,不影响初始性能 |
|
|
|
- ✅ **内存优化**: 只加载可见区域的缩略图,减少内存占用 |
|
|
|
|
|
|
|
### ImageGalleryPageViewModel 修复切换后图片空白问题 - 优化缩略图恢复策略 |
|
|
|
- **日期**: 2025年1月 |
|
|
|
- **修改内容**: 修复视图重新激活时图片显示空白的问题,优化缩略图恢复策略 |
|
|
|
- **问题分析**: |
|
|
|
- ❌ **切换后图片空白**: 视图停用时清理了 `_thumbnailSource`,重新激活时只重置标志位,但 `_thumbnailSource` 仍为 null,导致图片显示空白 |
|
|
|
- ❌ **恢复策略缺失**: 没有从缓存恢复缩略图的机制,导致重新激活时图片无法显示 |
|
|
|
- ❌ **性能问题**: 如果对所有项都立即恢复,可能会触发大量异步操作 |
|
|
|
- **修改文件**: |
|
|
|
- `AuroraDesk.Presentation/ViewModels/Pages/ImageGalleryPageViewModel.cs` (添加从缓存恢复缩略图的逻辑,优化恢复策略) |
|
|
|
- `AuroraDesk.Presentation/Views/Pages/ImageGalleryPageView.axaml.cs` (使用新的批量恢复方法) |
|
|
|
- **主要修复**: |
|
|
|
- ✅ **从缓存恢复**: 添加 `LoadThumbnailFromCacheAsync()` 方法,从缓存服务恢复缩略图 |
|
|
|
- ✅ **优化恢复策略**: 添加 `ResetAllThumbnailReferences()` 方法,分批恢复缩略图 |
|
|
|
- ✅ **立即恢复可见项**: 立即恢复前 100 个项(可见项),确保用户看到的图片立即显示 |
|
|
|
- ✅ **延迟恢复其他项**: 其他项延迟分批恢复(每批 100 个,每批之间延迟 50ms),避免一次性触发大量异步操作 |
|
|
|
- ✅ **自动重新加载**: 如果缓存中没有缩略图,自动触发重新生成 |
|
|
|
- **技术细节**: |
|
|
|
- 在 `ResetThumbnailReferenceCleared()` 中,重置标志位后调用 `LoadThumbnailFromCacheAsync()` |
|
|
|
- `LoadThumbnailFromCacheAsync()` 从缓存服务获取缩略图,如果缓存中有,直接使用;如果没有,触发重新生成 |
|
|
|
- `ResetAllThumbnailReferences()` 分批恢复:立即恢复前 100 个,其他项每批 100 个延迟恢复 |
|
|
|
- 每批之间延迟 50ms,让 UI 有机会渲染 |
|
|
|
- 恢复操作是异步的,不会阻塞 UI 线程 |
|
|
|
- **效果**: |
|
|
|
- ✅ **图片正常显示**: 视图重新激活时,图片能够正常显示,不再空白 |
|
|
|
- ✅ **性能优化**: 分批恢复策略确保 UI 流畅,不会因为大量异步操作导致卡顿 |
|
|
|
- ✅ **用户体验**: 可见项立即恢复,其他项逐步恢复,用户体验良好 |
|
|
|
|
|
|
|
### ImageGalleryPageViewModel 性能优化 - 修复大量图片时的性能问题和 Bitmap 访问异常 |
|
|
|
- **日期**: 2025年1月 |
|
|
|
- **修改内容**: 优化 ImageGalleryPageViewModel 在大量图片(7000+)时的性能问题,移除 ThumbnailSource getter 中的 PixelSize 检查 |
|
|
|
- **问题分析**: |
|
|
|
- ❌ **性能问题**: 当有大量图片(7000+)时,每次访问 `ThumbnailSource` getter 都会访问 `PixelSize` 属性来检查 Bitmap 是否有效,导致巨大的性能开销 |
|
|
|
- ❌ **UI 卡顿**: 切换 tab 时,大量图片的 getter 被频繁调用,每次都要访问 PixelSize,导致 UI 严重卡顿 |
|
|
|
- ❌ **异常抛出**: 在某些情况下,访问已释放的 Bitmap 的 PixelSize 会抛出异常 |
|
|
|
- **修改文件**: |
|
|
|
- `AuroraDesk.Presentation/ViewModels/Pages/ImageGalleryPageViewModel.cs` (优化 ThumbnailSource getter,移除 PixelSize 检查) |
|
|
|
- `AuroraDesk.Presentation/Views/Pages/ImageGalleryPageView.axaml.cs` (优化视图激活时的处理,避免立即加载所有缩略图) |
|
|
|
- **主要优化**: |
|
|
|
- ✅ **移除 PixelSize 检查**: 从 ThumbnailSource getter 中移除每次访问时的 PixelSize 检查,避免性能开销 |
|
|
|
- ✅ **使用标志位**: 添加 `_thumbnailReferenceCleared` 标志位来标记引用是否已被清理,代替访问 Bitmap 属性 |
|
|
|
- ✅ **优化 getter**: getter 现在只检查标志位,不访问 Bitmap 属性,性能大幅提升 |
|
|
|
- ✅ **视图激活优化**: 视图重新激活时只重置标志位,不立即加载所有缩略图,缩略图按需加载 |
|
|
|
- ✅ **添加重置方法**: 添加 `ResetThumbnailReferenceCleared()` 方法,用于视图重新激活时重置标志 |
|
|
|
- **技术细节**: |
|
|
|
- 使用 `_thumbnailReferenceCleared` 布尔标志位代替每次访问时的 PixelSize 检查 |
|
|
|
- getter 现在只检查标志位:`if (_thumbnailReferenceCleared) return null;` |
|
|
|
- 在 `ClearThumbnailReference()` 中设置标志位为 true,而不是访问 Bitmap |
|
|
|
- 在 `EnsureThumbnailLoaded()` 中,如果标志位为 true,重置标志位 |
|
|
|
- 在视图激活时,只调用 `ResetThumbnailReferenceCleared()` 重置标志,不调用 `EnsureThumbnailLoaded()`(避免加载所有缩略图) |
|
|
|
- 缩略图会在 UI 绑定时按需加载(通过虚拟化或滚动检测) |
|
|
|
- **效果**: |
|
|
|
- ✅ **性能大幅提升**: 移除 PixelSize 检查后,getter 访问速度提升数百倍 |
|
|
|
- ✅ **UI 流畅**: 切换 tab 时不再卡顿,即使有7000+个图片 |
|
|
|
- ✅ **资源安全**: 通过标志位和视图生命周期管理,仍然能确保资源安全 |
|
|
|
- ✅ **按需加载**: 缩略图按需加载,不会一次性加载所有图片 |
|
|
|
|
|
|
|
### ImageGalleryPageView Tab 切换异常修复 - 修复 CancellationTokenSource 生命周期管理问题 |
|
|
|
- **日期**: 2025年1月 |
|
|
|
- **修改内容**: 修复 ImageGalleryPageView 在第二次 tab 切换时发生的 CancellationTokenSource 已释放异常 |
|
|
|
- **问题分析**: |
|
|
|
- ❌ **CancellationTokenSource 生命周期问题**: 视图停用时清理了 CancellationTokenSource,但事件处理器(OnLayoutUpdated、OnScrollChanged)仍然订阅在 ScrollViewer 的事件上 |
|
|
|
- ❌ **事件处理器访问已释放资源**: 当布局更新或滚动事件再次触发时,事件处理器尝试取消一个已经被释放的 CancellationTokenSource,导致 `ObjectDisposedException` |
|
|
|
- ❌ **事件订阅未清理**: 视图停用时没有取消事件订阅,导致事件处理器在视图停用后仍可能被调用 |
|
|
|
- **修改文件**: |
|
|
|
- `AuroraDesk.Presentation/Views/Pages/ImageGalleryPageView.axaml.cs` (添加事件订阅清理和资源保护) |
|
|
|
- **主要修复**: |
|
|
|
- ✅ **添加停用标志**: 添加 `_isDisposed` 标志来跟踪视图是否已停用 |
|
|
|
- ✅ **事件订阅清理**: 在视图停用时,先取消 ScrollViewer 的事件订阅,然后再清理 CancellationTokenSource |
|
|
|
- ✅ **事件处理器保护**: 在 `OnLayoutUpdated` 和 `OnScrollChanged` 方法开始时检查 `_isDisposed` 标志,如果已停用则直接返回 |
|
|
|
- ✅ **异常处理**: 在调用 `CancellationTokenSource.Cancel()` 时添加 try-catch 来处理 `ObjectDisposedException` |
|
|
|
- ✅ **重新激活处理**: 在 `SetupScrollDetection` 中重置 `_isDisposed` 标志,并在重新设置事件订阅前先取消之前的订阅 |
|
|
|
- **技术细节**: |
|
|
|
- 使用 `_isDisposed` 标志在视图停用时设置为 true,防止事件处理器访问已释放的资源 |
|
|
|
- 在清理资源时,先取消事件订阅(`ScrollChanged` 和 `LayoutUpdated`),然后再清理 CancellationTokenSource |
|
|
|
- 在事件处理器中添加 `_isDisposed` 检查,如果已停用则立即返回 |
|
|
|
- 使用 try-catch 包装 `Cancel()` 调用,捕获 `ObjectDisposedException` 并忽略 |
|
|
|
- 在视图重新激活时,重置 `_isDisposed` 标志并重新设置事件订阅 |
|
|
|
- **效果**: |
|
|
|
- ✅ **Tab 切换正常**: 第二次及后续切换 tab 时不再抛出 ObjectDisposedException |
|
|
|
- ✅ **资源安全**: 事件订阅在视图停用时被正确清理,防止访问已释放的资源 |
|
|
|
- ✅ **异常处理**: 即使出现异常情况,也能安全处理,不会影响应用稳定性 |
|
|
|
|
|
|
|
### ImageGalleryPageViewModel Tab 切换异常修复 - 修复 Bitmap 生命周期管理问题 |
|
|
|
- **日期**: 2025年1月 |
|
|
|
- **修改内容**: 修复 ImageGalleryPageViewModel 在 tab 切换时发生的 MeasureOverride 异常 |
|
|
|
- **问题分析**: |
|
|
|
- ❌ **Bitmap 生命周期问题**: ImageMetadata 中的 ThumbnailSource 持有缓存中 Bitmap 的引用,当缓存清理时 Bitmap 被释放,但 ImageMetadata 仍持有引用 |
|
|
|
- ❌ **Tab 切换时异常**: 切换 tab 后,Image 控件尝试测量自己时访问已释放的 Bitmap,导致 `Avalonia.Controls.Image.MeasureOverride` 抛出异常 |
|
|
|
- ❌ **资源清理缺失**: 视图停用时没有清理 Bitmap 引用,导致切换 tab 时访问无效资源 |
|
|
|
- **修改文件**: |
|
|
|
- `AuroraDesk.Presentation/ViewModels/Pages/ImageGalleryPageViewModel.cs` (添加 Bitmap 有效性检查和清理方法) |
|
|
|
- `AuroraDesk.Presentation/Views/Pages/ImageGalleryPageView.axaml.cs` (添加视图激活/停用时的资源清理逻辑) |
|
|
|
- **主要修复**: |
|
|
|
- ✅ **Bitmap 有效性检查**: 在 ThumbnailSource getter 中添加有效性检查,访问 PixelSize 属性来检测 Bitmap 是否已被释放 |
|
|
|
- ✅ **清理缩略图引用**: 添加 `ClearThumbnailReferences()` 方法,在视图停用时清理所有 ImageMetadata 的 Bitmap 引用 |
|
|
|
- ✅ **视图生命周期管理**: 在 ImageGalleryPageView 的 WhenActivated 中,视图停用时清理引用,视图激活时重新加载可见项的缩略图 |
|
|
|
- ✅ **ImageMetadata 清理方法**: 添加 `ClearThumbnailReference()` 方法,安全地清理单个 ImageMetadata 的 Bitmap 引用 |
|
|
|
- **技术细节**: |
|
|
|
- 在 ThumbnailSource getter 中使用 try-catch 检查 Bitmap 是否有效(通过访问 PixelSize 属性) |
|
|
|
- 如果 Bitmap 已被释放,getter 返回 null 并清理内部引用 |
|
|
|
- 在视图停用时调用 `ClearThumbnailReferences()` 清理所有引用,防止访问已释放的资源 |
|
|
|
- 在视图激活时重新调用 `EnsureThumbnailLoaded()` 重新加载可见项的缩略图 |
|
|
|
- Bitmap 由 ThumbnailCacheService 管理生命周期,ImageMetadata 只持有引用,不负责释放 |
|
|
|
- **效果**: |
|
|
|
- ✅ **Tab 切换正常**: 切换 tab 时不再抛出 MeasureOverride 异常 |
|
|
|
- ✅ **资源安全**: Bitmap 引用在视图停用时被清理,避免访问已释放的资源 |
|
|
|
- ✅ **自动恢复**: 视图重新激活时自动重新加载缩略图 |
|
|
|
- ✅ **性能优化**: 只在必要时检查 Bitmap 有效性,避免不必要的性能开销 |
|
|
|
|
|
|
|
### 项目文件结构重组 - 移动关键文件到项目根目录 |
|
|
|
- **日期**: 2025年1月 |
|
|
|
- **修改内容**: 将 `.git`、`.gitignore`、`AuroraDesk.sln` 和所有 `.md` 文档文件从 `AuroraDesk` 子目录移动到项目根目录 `MyAvaloniaApp` |
|
|
|
|