Mokona Guu Center

Android : affichage et profiling à tâtons

Publié le

En regardant la documentation, les exemples et les codes sources disponibles sur Android, on s'aperçoit qu'il y a beaucoup de manières différentes de mettre à jour un affichage. Je vais devoir en choisir un et ne pas me fermer la porte aux autres.

Affichage

En effet, j'ai à présent un serpent qui bouge, il faut donc que je mette à jour l'affichage de l'état du jeu. J'ai déjà fait une interface dans le programme avec dans l'idée que je pourrais un jour passer d'une méthode à une autre, mais sans connaître la plateforme dans les détails, je ne suis pas certain d'avoir tout prévu.

Pour le moment, j'affiche en passant par un Canvas qui m'est envoyé dans le onDraw() de ma View. C'est une des méthodes les plus simples. Est-elle adaptée, je n'en sais rien.

Dans un premier temps, je me contente d'invalider la surface dès que j'ai terminé mon affichage : à la fin du onDraw(), j'appelle invalidate(). J'ai le sentiment que ce n'est pas une méthode terriblement bonne. Que se passe-t-il quand j'appelle invalidate() ? Est-ce que je ne risque pas de tirer un peu trop sur les ressources de la machine ?

Je vois un autre soucis : puisque le contrôle du jeu se fait depuis le onDraw() de la View, je suis dans un contexte d'affichage. D'après les informations que j'ai pu glaner, j'ai plutôt intérêt à ne pas trop bloquer dans cette méthode et à rendre la main au système « rapidement ».

On sent venir le thread séparé qui va s'occuper de la mise à jour du jeu...

Comme j'ai besoin que cela bouge à l'écran et que je n'ai pas forcément envie de jouer une séquence à chaque fois, j'implémente rapidement une petite IA de serpent qui suit les bords de la grille.

Profiling

J'ai donc besoin de savoir ce qu'il se passe sur la machine. Pour cela, le SDK propose plusieurs choses : DDMS, qui se charge de se connecter à la machine (l'émulateur dans mon cas) pour aller y récupérer des informations sur la mémoire, les threads ; un outil de profiling, TraceView ; une possibilité de dump HPROF.

Pour le moment, je ne fais pas assez de choses dans le jeu pour que la mémoire ait une importance. Cela alloue quand même déjà 3 Mo, probablement le système.

Je commence par m'assurer de quelque chose : est-ce que la méthode du invalidate() dans le onDraw() génère beaucoup trop d'appels. Pour cela, je mesure le temps entre deux appels à onDraw à l'aide de SystemClock.uptimeMillis(). J'obtiens une moyenne de 1/60ième de seconde avec des périodes stables et quelques pics à 1/30ième et quelques fois à 1/20ième. Mais c'est assez stable.

je ne sais pas si le rafraîchissement au 1/60ième de seconde est une constante sur les différents téléphone, mais en tout cas, je vois que l'OS met l'invalidation de la surface dans une fil d'attente pour la frame suivante. C'est une bonne chose, cela signifie que même cette méthode basique ne va pas faire écrouler les ressources du téléphone.

Un petit coup de HPROF me montre quelque chose : je ne consomme rien en mémoire par rapport aux objets Android que je consomme. Il y a un poids de base pour une application Android qui n'est pas à négliger.

Passons à Traceview. J'encadre le code de onDraw() avec les fonctions de démarrage et d'arrêt de profiling. Première constatation, ça ralenti pas mal l'exécution. Seconde constatation, ma fonction de déplacement gère le « delta t » sans borne maximale. Du coup, je me trouve avec des consommation aberrantes dans la mise à jour des déplacements.

Petite modification donc : je limite le « delta t » maximum.

Que me dit alors le profiler : que je passe la moitié du temps à afficher la grille de debug...

Je profile alors entre onStart() et onStop() pour avoir une vue d'ensemble de l'application. Le résultat est que 50% du temps de l'application est passée dans le framework Android, et le reste dans mon application. Sur émulateur, j'ai calculé que mon temps de mise à jour du jeu était en peu en dessous de 2ms. Ce qui donne un poids côté Android de 2ms.

On me souffle cependant qu'un vrai téléphone est plus rapide que l'émulateur.