-
Notifications
You must be signed in to change notification settings - Fork 3
Performance
As with any application people, performance is key. When people are using a mobile application, they normally perform tasks on the go and they depend on their application to stay connected. People might start discarding a poor performing application, especially if they depend on it to help them in situations where they don't have much time to react. DIPS creates applications for the Norwegian Health Care, where the user group is people in situations like this. We want to provide some a helping hand in focusing on performance with this wiki page.
All examples need needs you to set
DUI.IsDebug=true;
this should be done with by you inMauiProgram
with an#if DEBUG
. Please see Getting Started for a complete example.
The time it takes for your page to start reacting is essential for the performance of a mobile application. You do not want to end up with people having to wait for your pages to render while they navigate, which do feel like the application is "freezing" while navigating.
In order for you to do profiling of your pages loading, we have added a ShouldLogLoadingTime
property that can be used if you are using ContentPage . This will log the time it took from the page was constructed to the page was loaded. This will most likely highlight the needs for "lazy loading" of views or choosing the correct component for your page. We hope it will be helpful!
Memory leaks are essential to get rid of for the application to perform at its best. MAUI has provided a Wiki page with information regarding memory leaks, to help you better understand memory leaks. In addition to this we have provided a set of helping methods in DIPS.Mobile.UI.
DIPS.Mobile.UI provides multiple ways for you to check if an object was garbage collected. An objects finalizer will run when an objects gets collected by the garbage collector. Garbage collections happens at different times during the application lifecycle, but you can force garbage collecting. This is recommended to only do while debugging.
Both ContentPage
and a ViewModel
can log when its finalizer has ran. To opt-in, use the ShouldLogWhenGarbageCollected
property.
We have added GarbageCollection that wraps the GC from .NET. Use this to force collection and wait for finalizers.
ContentPage
can monitor garbage collection when navigated to. To opt-in, set the ShouldGarbageCollectAndLogWhenNavigatedTo
for a page of your choice. This will print the current Garbage Collection total memory. If you have set the ShouldLogWhenGarbageCollected
for other pages that people can navigate out of, they should log. If they do not, they are zombies and live forever.
Shell
has the ability to garbage collect when a Pop
, PopToRoot
, Remove
or ShellItemChanged
has been initiated from people or programatically. This will monitor the page that people navigated to from the a page, and try to garbage collect and monitor it. It will print if was garbage collected, or not. To opt in, use the ShouldGarbageCollectPreviousPage
.
If you want to monitor a specific object when it should be garbage collected (like when an object in a page should have been destroyed), use the GCCollectionMonitor
class.
Here is an example of when NavigationPage
runs Pop
and you want to monitor the previous page.
private void NavPageOnPopped(object? sender, NavigationEventArgs e)
{
m_monitor.Observe(e.Page);
m_monitor.CheckAliveness();
}
To clean up your views, you need to subscribe to events from the framework that will let you do that. This section describes the possibilities of using UnLoaded
events.
Use UnLoaded
to clean up memory leaks. Most commonly is to unsubscribe to events and to make sure you do not have a reference to an object that lives longer than the page.
There is no easy way for you to clean up for views and handlers compared to pages. There is a issue in .NET MAUI that hopefully will make the developer experience easier for you: https://github.com/dotnet/maui/issues/16332
Due to the different times UnLoaded
runs for the platforms, we have to mention Loaded
as well.
Here is a summary of when the events are invoked:
Loaded is invoked when:
- The page is loaded the first time. (Both platforms)
- When the element is visible in a CollectionView. (Both platforms)
- Hot Reloading. (Both platforms)
- The page was navigated back to. (iOS)
- The page is navigating to a new page (iOS)
- UnLoaded is invoked after.
UnLoaded is invoked when:
- The page was navigated out of. (Both platforms)
- Invoked 2x times when the page was navigated to a new page. (iOS)
- When the element disappears from a CollectionView. (Both Platforms)
This makes Loaded
and UnLoaded
potential events to subscribe to events/add observers and to remove them. But we recommend that you test this with the monitoring and to test the view thoroughly as the events has different invocations for the platforms, which can lead to bugs.
When working with VisualElements
, or more commonly ContentView
, use the Loaded
and UnLoaded
events to subscribe and unsubscribe.
public MyView()
{
Loaded += Load;
Unloaded += UnLoad;
}
private void Load(object? sender, EventArgs e)
{
//Subscribe to whatever
}
private void UnLoad(object? sender, EventArgs e)
{
//Unsubsribe to whatever
//Clean up resources
}
No need to unsubscribe to
Loaded
andUnLoaded
as these will not give you a memory leak.
- Follow the "When working with
VisualElement
approach for your virtual view. - Create
Connect()
andDisconnect()
partial methods in your handler. Implement them in the platforms. - Delegate
Connect()
andDisconnect()
to the handler forLoaded
andUnLoaded
events.
public MyView()
{
Loaded += Load;
Unloaded += UnLoad;
}
private void Load(object? sender, EventArgs e)
{
if (Handler is MyViewHandler myViewHandler)
{
myViewHandler.Connect();
}
}
private void UnLoad(object? sender, EventArgs e)
{
if (Handler is MyViewHandler myViewHandler)
{
myViewHandler.Disconnect();
}
}
Shared handler:
public partial class MyViewHandler : ContentViewHandler
{
internal partial void Connect();
internal partial void Disconnect();
}
Platform handler:
public partial class MyViewHandler
{
internal partial void Connect()
{
//Subscribe to whatever
}
internal partial void Disconnect()
{
//Unsubsribe to whatever
//Clean up resources
}
}