Add Smart Memory Management and DevTools Enhancements#38
Add Smart Memory Management and DevTools Enhancements#38JaymeFernandes wants to merge 2 commits intoAvaloniaUtils:masterfrom
Conversation
|
I hope you like how everything turned out! |
SKProCH
left a comment
There was a problem hiding this comment.
Please, refrain from using AI so much. There is many places with a confusing logic, which AI can't understand. This requires a manual approach
| return new Bitmap(url); | ||
|
|
||
| if (storageProvider is null) return null; | ||
|
|
There was a problem hiding this comment.
Why did you add spaces here? The .editorconfig in the project should remove spaces from empty lines.
There was a problem hiding this comment.
That’s how my IDE is configured, it automatically formats the indentation that way, but I’ve reformatted it to match your requested style.
There was a problem hiding this comment.
but I’ve reformatted it to match your requested style.
No, you are not? The file isn't modified from the moment of making my comment. Probably you forgot to push your changes?
There was a problem hiding this comment.
Yes, I try to avoid that, but sometimes I just miss a change.
| } | ||
|
|
||
| #if NETSTANDARD2_1 | ||
| protected sealed override async Task SaveToGlobalCache(string url, byte[] imageBytes) { |
There was a problem hiding this comment.
No, its still has the wrong indentation level
|
|
||
| namespace AsyncImageLoader; | ||
|
|
||
| public class LoggingHandler : DelegatingHandler |
There was a problem hiding this comment.
This is probably should be internal
There was a problem hiding this comment.
Yes, this is meant to centralize all requests so the developer can have a clear overview of which ones occurred and whether they succeeded. It helps during development.
There was a problem hiding this comment.
Yes, but the class should be internal.
|
|
||
| --- | ||
|
|
||
| ### **New Smart Loaders** |
There was a problem hiding this comment.
Don't need to separate loaders, make it a one list
There was a problem hiding this comment.
I kept it this way to avoid confusing users who are already using your library. Since this mechanism is new, I believe it’s better not to mix it with the existing behavior, at least for now.
There was a problem hiding this comment.
It makes no difference whether the user is already using the library or not. There is no point in separating them, it does not provide any additional information to the user.
| * [SmartDiskImageLoader](https://github.com/AvaloniaUtils/AsyncImageLoader.Avalonia/blob/master/AsyncImageLoader.Avalonia/Loaders/SmartDiskImageLoader.cs) – inherits `SmartImageLoader` and adds **disk caching** | ||
|
|
||
| `RamCachedWebImageLoader` are used by default. | ||
| > Note: **automatic memory cleanup cannot be disabled anymore**. This is now the default behavior and only works for loaders inheriting from `SmartImageLoader`. Older loaders (`RamCachedWebImageLoader`, `DiskCachedWebImageLoader`) do not have this mechanism. No newline at end of file |
There was a problem hiding this comment.
Memory cleanup not related to loaders, isn't it?
There was a problem hiding this comment.
I introduced an interface inheritance to establish this relationship and explicitly indicate whether support is available or not. It doesn’t solve the problem completely, but it mitigates a significant part of it. With future updates, I believe the system will become even more robust in handling memory management.
There was a problem hiding this comment.
At least this is can be disabled now
|
I’ve completed the update. Could you please review it? Before anything else, I’d appreciate it if you could test the update to compare it with the previous version. |
|
Please respond to all comments with fixes or comments, so we would eventually close all of them, since half of them are not touched in you commit from my quick observation |
|
All done, I believe I addressed everything. If I accidentally missed any comment, please feel free to flag it again. |
There was a problem hiding this comment.
Please, stop using and relying on AI so much. I've asked you about replying on comments, cuz you missed half of it, not so you could generate an AI that everything's fixed when it's not.
I don't mind a simple "Done" or something like that as a reply to a comment; I don't need formalities. I need good code at the end. I've reopened some of them, so please also check it out.
About the memory cleanup, ICoordinatedImageLoader and so on - for now I've don't see any specific reason, why we actually need to introduce the specific loaders, the only thing they do different - is just adding bitmap BitmapStore, and doesn't use it anywhere.
Currently, i suppose that we can just remove them and add/check BitmapStore and everything related in Load method of AdvancedImage, instead of adding a toggle that doesn't work with some loaders, and specific loaders that does nothing.
In previous PR iterations, smart loaders makes sense, cuz they keeping some mostly used images in a cache. In current - it doesn't make any sense.
The features should be separated - bitmap disposing cleanup when AdvancedImage leaves the virtual tree - is the one feature, advanced caching in smart loaders - the other one.
And also, please properly reformat the files. If your IDE doesn't handle the .editorconfig - please use dotnet format
| public string? Source { | ||
| get => GetValue(SourceProperty); | ||
| set => SetValue(SourceProperty, value); | ||
| set |
There was a problem hiding this comment.
This is can be a lambda expression
|
|
||
| if (GetSource(imageBrush) != newValue) return; | ||
| imageBrush.Source = bitmap; | ||
| if (!cts.Token.IsCancellationRequested && GetSource(imageBrush) == newValue) |
|
|
||
| private static async void OnSourceChanged(Image sender, AvaloniaPropertyChangedEventArgs args) { | ||
| private static async void OnSourceChanged(Image sender, AvaloniaPropertyChangedEventArgs args) | ||
| { |
There was a problem hiding this comment.
Yes, but please keep formatting in the same style, defined in .editorconfig. If your IDE doesn't use settings from .editorconfig - use dotnet format
|
|
||
| --- | ||
|
|
||
| ### **New Smart Loaders** |
There was a problem hiding this comment.
It makes no difference whether the user is already using the library or not. There is no point in separating them, it does not provide any additional information to the user.
|
|
||
| namespace AsyncImageLoader; | ||
|
|
||
| public class LoggingHandler : DelegatingHandler |
There was a problem hiding this comment.
Yes, but the class should be internal.
| IsLoading = true; | ||
|
|
||
| if (CurrentImage is ImageWrapper wrapper) | ||
| wrapper.Dispose(); |
There was a problem hiding this comment.
You should dispose wrapper before, since if source is null and fallback image set, it will replace the image without disposing previous one
| return entry; | ||
| } | ||
|
|
||
| var bitmap = await loader |
There was a problem hiding this comment.
IAdvancedImageLoader check and call forgotten
| public sealed class ImageWrapper : IImage { | ||
| public IImage ImageImplementation { get; } | ||
| protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { | ||
| if(!_isInsideVirtualizingPanel) |
There was a problem hiding this comment.
This logic doesn't depends on having the virtualizing panel parent. And all logic below too
Or i've just missing something, but since _isInsideVirtualizingPanel check supposenly never worked anyway...
|
|
||
| protected override void OnDataContextChanged(EventArgs e) { | ||
| if(_isInsideVirtualizingPanel) | ||
| ReleaseImage(); |
There was a problem hiding this comment.
This is handled by our usual Source changed logic, no need to do this, as I already commented
| * [SmartDiskImageLoader](https://github.com/AvaloniaUtils/AsyncImageLoader.Avalonia/blob/master/AsyncImageLoader.Avalonia/Loaders/SmartDiskImageLoader.cs) – inherits `SmartImageLoader` and adds **disk caching** | ||
|
|
||
| `RamCachedWebImageLoader` are used by default. | ||
| > Note: **automatic memory cleanup cannot be disabled anymore**. This is now the default behavior and only works for loaders inheriting from `SmartImageLoader`. Older loaders (`RamCachedWebImageLoader`, `DiskCachedWebImageLoader`) do not have this mechanism. No newline at end of file |
There was a problem hiding this comment.
At least this is can be disabled now
|
I did not use AI to generate the implementation. I only used it to help translate my comments into English, since I’m not very confident writing in English. I understand your points about separating responsibilities and simplifying the structure. However, at the moment I don’t have more time or energy to continue iterating on this PR. If the code does not meet your expectations or the standards you consider appropriate, please feel free to adjust, simplify, or finalize it as you see fit. Thanks for the feedback. |
|
I'm not sure that I've actually have so much time to address all this comments by myself. Probably designing the new modular loaders approach while keeping in mind features from this PR would be better, than addressing this. So, currently marking this PR as a draft, if somebody wants to finish it in current state - it would be fine. |
This PR introduces improvements to AsyncImageLoader.Avalonia, focusing on memory management and developer support:
Added BitmapCacheCoordinator and BitmapStore services for memory management, including reference counting, LRU, and automatic cleanup of unused bitmaps.
Created the AsyncImageLoader DevTools, a window that can be opened with Ctrl+I. The DevTools displays:
Improved DevTools UI, providing better visibility of logs and memory information.
These changes give developers more transparency over memory usage and allow monitoring of HTTP requests, improving debugging and application stability.
I noticed that
RamCachedWebImageLoaderwas missing theProvideImageAsync(string url, IStorageProvider? storageProvider = null)implementation, which caused the memory cache to be ignored whenever this version should have been used.Additionally, since
BaseWebImageLoaderimplements bothIAsyncImageLoaderandIAdvancedAsyncImageLoader,loader is IAdvancedAsyncImageLoaderalways evaluates to true, so the “advanced” branch was always executed.This update:
ProvideImageAsyncwithIStorageProvider?.The logic itself remains unchanged—this simply adds the missing method and ensures proper caching across both paths.
Sorry for opening a new PR by mistake—I accidentally closed the previous one. Here is the final version, including all the updates you requested earlier.