Top 5 des considérations de performances pour les développeurs .net Maui

Suivre ces cinq conseils vous aidera à créer des applications Maui .NET que davantage d’utilisateurs peuvent profiter sans problèmes de performances.
Dans le monde du développement mobile, il est très important de toujours considérer que tous les utilisateurs n’ont pas de téléphones haut de gamme. Par conséquent, il est conseillé de suivre les meilleures pratiques pour éviter les goulots d’étranglement ou les problèmes qui peuvent affecter les performances des applications.
Dans cet article, je partagerai cinq considérations que vous devez suivre lors du développement d’applications avec .net Maui.
Sans aucun doute, l’une des erreurs les plus courantes commises dans le développement mobile en général consiste à utiliser les commandes de cadre mal, combinant des éléments d’une manière que nous ne devrions pas. C’est comme essayer de manger un steak avec une scie: bien que nous puissions éventuellement atteindre l’objectif final de le couper et de le manger, cela prendrait probablement beaucoup plus de temps et serait inconfortable et, surtout, il existe de meilleurs outils qui nous permettaient de le faire plus efficacement.
Image créée avec Chatgpt
Évitez la nidification profonde ou la mauvaise utilisation des dispositions
L’un des premiers problèmes de performances qu’un développeur .net Maui rencontre est lors de l’utilisation de Nested Empiler dispositions. En interne, ces dispositions effectuent une série de recalculs chaque fois qu’un changement se produit dans l’interface graphique. Par exemple, lorsqu’un nouvel élément est ajouté à la pile ou lorsque l’un d’eux est caché via le IsVisible
propriété.
Comme vous pouvez l’imaginer, s’il y a des piles imbriquées et qu’un changement se produit dans l’un d’eux, l’espace des éléments de pile d’enfants et que les parents doivent être recalculés, entraînant une dégradation des performances. La solution à ce problème est de remplacer le imbriqué StackLayout
avec des dispositions plus efficaces pour cette tâche, comme une grille.
Un exemple de imbriqué StackLayout
Les éléments sont les suivants:
<VerticalStackLayout Padding="10">
<HorizontalStackLayout Spacing="5">
<Image
HeightRequest="40"
Source="dotnet_bot.png"
WidthRequest="40" />
<VerticalStackLayout>
<Label Text="Full Name" />
<Label
FontSize="Small"
Text="Role"
TextColor="Gray" />
</VerticalStackLayout>
</HorizontalStackLayout>
<Button Text="Follow" />
</VerticalStackLayout>
Cela peut être optimisé à l’aide d’une grille comme indiqué ci-dessous:
<Grid
Padding="10"
ColumnDefinitions="40, *"
ColumnSpacing="10"
RowDefinitions="20, 30, 50"
RowSpacing="5">
<Image Grid.RowSpan="2" Source="dotnet_bot.png" />
<Label
Grid.Column="1"
FontAttributes="Bold"
Text="Full Name" />
<Label
Grid.Row="1"
Grid.Column="1"
FontSize="Small"
Text="Role"
TextColor="Gray" />
<Button
Grid.Row="2"
Grid.ColumnSpan="2"
Text="Follow" />
</Grid>
Ne nidisez pas les commandes de défilement dans un stacklayout
Un autre problème courant lorsque vous travaillez avec les dispositions est de tenter de nicer les contrôles conçus pour faire défiler sans restrictions, tels que CollectionView
ou ScrollView
dans tout type de StackLayout
comme indiqué dans l’exemple suivant:
<VerticalStackLayout>
<CollectionView...>
</CollectionView>
</VerticalStackLayout>
C’est parce que le StackLayout
est conçu pour ne pas nécessiter de taille de ses éléments enfants; Cela leur donne simplement l’espace dont ils ont besoin. Le problème est que les contrôles comme CollectionView
et ScrollView
Demandez toute la taille disponible, empêchant le Stacklayout de déterminer correctement l’espace qu’il devrait fournir. La solution est simple: évitez ce type de nidification dans l’interface utilisateur.
Sauter l’ajout de multiples reconnaissances en gestes imbriquées
Lors de la création de vos applications multiplateformes, vous pouvez vous retrouver dans une situation où vous souhaitez ajouter une détection de TAP à un contrôle qui ne l’a pas nativement, comme un Image
contrôle. Dans ces cas, le TapGestureRecognizer
Le cours vient à la rescousse. Le problème se pose lorsqu’un développeur ajoute plusieurs imbriquées TapGestureRecognizer
Éléments, comme dans l’exemple suivant:
<Grid>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OuterTapCommand}" />
</Grid.GestureRecognizers>
<Image...>
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding InnerTapCommand}" />
</Image.GestureRecognizers>
</Image>
</Grid>
En raison de la nature de propagation de ces éléments, lorsqu’un utilisateur puise un élément à l’écran, la propagation commence à partir du niveau hiérarchique le plus élevé et se déplace dans l’arbre visuel, à la recherche du contrôle idéal pour gérer l’action. Cependant, si deux contrôles ou plus dans la hiérarchie TapGestureRecognizer
Éléments, un conflit se produit, car il n’est pas facile de déterminer quel contrôle devrait gérer l’événement.
La recommandation est d’éviter d’utiliser des reconnaissances gestiques sur des éléments imbriqués si la disposition ou le contrôle des parents en a déjà un attribué.
Optimiser le démarrage
Dans le monde mobile, il y a quelque chose appelé la période d’activation, qui est le temps entre le début de l’application et le moment où il est prêt à l’emploi. Nous devons essayer de le minimiser autant que possible afin que l’utilisateur puisse commencer à travailler avec l’application le plus rapidement possible.
Image créée avec Chatgpt
Il existe plusieurs façons d’optimiser la période d’activation. Par exemple, une erreur courante lorsque vous travaillez avec des listes dans .net Maui consomme un service de repos qui récupère tous les enregistrements à la fois. Ce processus peut prendre beaucoup de temps et perdre des ressources inutiles à mesure que la taille de la collection augmente.
Une solution possible pour ces cas consiste à charger uniquement les premiers enregistrements qui doivent être affichés, permettant à l’utilisateur de charger les autres enregistrements à la demande et en parties. Certains contrôles comme le progrès de la collection Telerik permettent Chargement de données à la demande natif dans .net Mauifaciliter cette tâche.
De même, si vous avez besoin de charger plus d’enregistrements, vous pouvez utiliser un contrôle connecté à votre contrôle de liste avec un effet chatoyant comme Maui.Squelette Pendant que les informations sont le chargement, ce qui permettra à l’utilisateur de savoir que le processus de chargement se produit sans bloquer l’interface graphique.
Crédit d’image: https://github.com/horussoftware
Une solution pour d’autres types de contrôles qui doivent récupérer des données ou des fichiers externes pourraient être d’ajouter des fichiers intégrés dans l’application qui servent d’espaces réservés pendant que les données finales sont chargées, ce qui permet un chargement beaucoup plus rapide.
En dernier recours, si vous devez charger beaucoup d’informations lors du démarrage de l’application, assurez-vous Afficher les animations de chargeur Pour que l’utilisateur sache que quelque chose se passe dans les coulisses.
Enfin, bien que l’utilisation d’un conteneur d’injection de dépendance offre un meilleur moyen de gérer les références, nous devons être prudents lorsque vous les utilisez car ils ajoutent une couche de complexité en utilisant la réflexion pour créer des instances d’objet.
Dans .NET MAUI en particulier, cela peut avoir un impact négatif sur le démarrage en cas de dépendances imbriquées ou si ces dépendances sont reconstruites sur chaque navigation entre les pages. Une solution consiste à utiliser des usines manuelles, comme le montre l’exemple suivant:
MessageServiceFactory.cs
public static class MessageServiceFactory
{
private static IMessageService? _instance;
public static IMessageService Create()
=> _instance ??= new MessageService();
}
Mainpage.xaml.cs
private void OnShowMessageClicked(object sender, EventArgs e)
{
var service = MessageServiceFactory.Instance;
lblMessage.Text = service.GetMessage();
}
Avec le code ci-dessus, nous évitons d’utiliser la réflexion, ce qui peut aider à améliorer l’initialisation de l’application.
Utiliser efficacement la mémoire et les ressources
Il y a plusieurs dangers auxquels nous sommes confrontés si nous n’optimions pas l’utilisation des ressources sur les appareils mobiles. Certains des plus courants sont la décharge de la batterie, la congélation de l’interface graphique, ou même, dans un cas plus extrême, la fermeture de l’application par le système d’exploitation. Cela provoque généralement une expérience utilisateur horrible, et nous devons l’éviter à tout prix.
Image créée avec Chatgpt
Une bonne pratique que nous devons suivre en développement général avec .net est d’envelopper IDisposable
objets using
Sections pour publier des ressources lorsqu’elles ne sont plus nécessaires, telles que les fichiers, les flux, etc., comme indiqué ci-dessous:
using (var writer = File.CreateText(filePath))
{
await writer.WriteLineAsync("Hello from .NET MAUI!");
}
Une autre chose que vous devez éviter est de saturer les dictionnaires mondiaux de ressources avec des ressources que nous ne partageons pas au niveau de l’application, mais essayez plutôt de conserver au niveau de la page ceux qui ne sont utilisés que sur cette page.
Une erreur courante consiste à s’abonner à des événements d’une autre classe ou à des événements des contrôles en dehors du cycle de vie de la page, ce qui conduit à la création de références solides qui empêchent la collecte des ordures appropriée.
L’idéal dans ces cas est de désabonner des classes lorsqu’ils ne sont plus utilisés (par exemple, lors de la sortie de la page). Une alternative consiste à utiliser des références faibles. Par exemple, si nous voulions communiquer entre deux objets, nous pourrions utiliser Boîte à outils MVVM Pour éviter les fuites de mémoire, fournissez de bonnes performances et obtenez un découplage naturel, comme indiqué ci-dessous:
Expéditeur.cs
public class Sender
{
public void Send(string text)
=> WeakReferenceMessenger.Default.Send(new TextNotification(text));
}
Récepteur.cs
public class Receiver
{
public Receiver()
{
WeakReferenceMessenger.Default.Register<TextNotification>(
this,
(r, msg) =>
{
Console.WriteLine($"Receiver got: {msg.Value}");
});
}
}
Il est également recommandé de retarder la création d’objets jusqu’à ce qu’ils soient utilisés, ce qui se traduit par une amélioration des performances. Cela peut être réalisé en utilisant le Lazy<T>
type, ce qui permet l’initialisation d’un objet lors de l’accès au Value
propriété. Si vous ne voulez pas utiliser le Lazy<T>
objet, vous pouvez utiliser Func<T>
l’enregistrement dans le conteneur de dépendance .net Maui comme indiqué ci-dessous:
Mauprogram.cs
builder.Services.AddSingleton<IHeavyService, HeavyService>();
builder.Services.AddSingleton<Func<Task<IHeavyService>>>(sp =>
async () =>
{
var svc = sp.GetRequiredService<IHeavyService>();
await svc.InitializeAsync();
return svc;
});
Mainviewmodel.cs
public partial class MainViewModel : ObservableObject
{
private readonly Func<Task<IHeavyService>> _getHeavy;
public MainViewModel(Func<Task<IHeavyService>> getHeavy)
=> _getHeavy = getHeavy;
[RelayCommand]
public async Task ExecuteHeavyTaskAsync()
{
var svc = await _getHeavy();
await svc.DoWorkAsync();
}
}
Enfin, il est possible d’utiliser un linker appelé ILLink
qui supprime les méthodes, les propriétés, les champs, etc., qui ne sont pas utilisées dans l’application, permettant une taille d’application plus petite. Ceci est disponible pour Androïde, ios et Catalyseur MAC.
Gardez l’interface utilisateur lisse
Un point crucial pour éviter de donner aux utilisateurs une mauvaise expérience lors de la création d’applications est de faire bon usage de l’asynchronie.
Image créée avec Chatgpt
L’une des premières choses à considérer est d’éviter de faire un travail lourd sur le fil d’interface utilisateur en le déchargeant sur un fil d’arrière-plan afin que l’interface utilisateur ne soit pas bloquée.
Par exemple, j’ai rencontré une fois une situation où j’avais besoin d’utiliser une bibliothèque externe qui m’a permis de générer des documents PDF à partir d’un fichier Excel; Cependant, toutes les API étaient synchrones et je n’avais pas accès pour modifier le code. Lors du test de l’application, puisque la méthode synchrone a pris plusieurs secondes pour créer le document, l’interface utilisateur a gelé.
Une façon de résoudre ce problème est d’utiliser un Task.Run
Bloquer pour exécuter les opérations synchrones sur un thread de pool de thread, ce qui empêchera l’interface utilisateur de geler, comme indiqué ci-dessous:
await Task.Run(() =>
{
PdfGenerator.GenerateFromExcel(excelPath, pdfPath);
});
Une autre recommandation consiste à regrouper plusieurs appels consécutifs à un service. Par exemple, j’avais un cas avec un client qui devait générer des fichiers audio à partir de paragraphes dans un document texte. Puisqu’il a fait un service à la fois, il a fallu plusieurs minutes à l’application pour traiter un seul document.
La solution consistait à appliquer le parallélisme dans les tâches, ce qui a fait gagner du temps et a libéré le fil d’interface utilisateur plus tôt. Vous trouverez ci-dessous un exemple de code illustrant ceci:
async Task ProcessParagraphAsync(string text, int index)
{
var audioBytes = await audioService.GenerateAudioFromTextAsync(text);
var outputPath = Path.Combine(outputFolder, $"paragraph_{index + 1}.mp3");
await File.WriteAllBytesAsync(outputPath, audioBytes);
}
var paragraphs = document.SplitIntoParagraphs();
var audioTasks = new List<Task>();
for (int i = 0; i < paragraphs.Count; i++)
{
audioTasks.Add(ProcessParagraphAsync(paragraphs[i], i));
}
await Task.WhenAll(audioTasks);
De même, il est conseillé d’utiliser le Boîte à outils MVVMcar il utilise en interne le AsyncCommand
modèle pour travailler avec les commandes. Ce modèle a un état interne qui vous permet de savoir si une tâche est exécutée ou non. En plus, AsyncCommand
Capture une erreur si l’on se produit et nous permet d’enregistrer un gestionnaire global ou d’afficher un message d’erreur.
Enfin, évitez de bloquer l’interface utilisateur vous-même en utilisant des mécanismes comme Task.Wait
, Task.Result
ou GetAwaiter().GetResult
comme ceux-ci peuvent provoquer des blocs de bloces.
Mesurer et optimiser
La dernière considération de performance pour les applications Maui .NET est que vous devez analyser soigneusement les méthodes génèrent le plus de charges sur le processeur ainsi que les goulots d’étranglement potentiels.
Image créée avec Chatgpt
La première recommandation consiste à toujours effectuer des tests approfondis sur les téléphones de milieu de gamme et bas de gamme pour voir comment votre application fonctionne dans des scénarios à haute demande.
De même, l’équipe .net Maui a compilé une série d’outils pour Profile Applications .net Mauitel que dotnet-trace et Perfoir. Ces outils vous fourniront un meilleur aperçu des goulots d’étranglement potentiels dans l’application, ainsi que des problèmes de démarrage et des fuites de mémoire qui peuvent exister.
Enfin, la communauté des développeurs .net Maui a des projets intéressants comme le Dotnet.meteor Projet, une extension de code vs pour l’exécution, le débogage et le profilage des applications MAUI .NET. De plus, vous pouvez également utiliser le MemoryToolkit.maui Projet pour vérifier les fuites de mémoire dans vos applications .net Maui.
Conclusion
Tout au long de cet article, vous avez appris cinq recommandations de performances pour garder à l’esprit lors du développement d’applications avec .net Maui. Suivez ces meilleures pratiques, visez à vous mettre dans les chaussures de l’utilisateur et s’efforcez toujours de fournir la meilleure expérience utilisateur possible.
Source link