Tutoriel pour apprendre à déboguer son application Android avec Logcat et Android Studio

À la découverte de l'outil le plus important d'Android Studio

Apprendre à déboguer son application est là base , c'est probablement la seule tâche que vous répéterez sans cesse. Il est donc essentiel de maîtriser cette étape.

3 commentaires Donner une note à l'article (5)

Article lu   fois.

L'auteur

Profil Pro

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

C'est inévitable, lorsque l'on développe on fait des bogues (bugs). Personne n'y échappe, même les meilleurs. Il est donc primordial d'être en mesure de trouver et comprendre les bogues pour pouvoir les résoudre.

Android Studio propose tous les outils nécessaires pour déboguer votre code. Cet article se limitera au code Java, mais sachez qu'il est également possible de déboguer du code C ou C++ ainsi que du Kotlin.

Nous aborderons uniquement l'utilisation du Logcat qui est la première étape du débogage. Vous pourrez ensuite aller encore plus loin à l'aide de points d'arrêt et du mode pas à pas.

II. Prérequis

Pour déboguer il faut un appareil qui le permet. Sur votre téléphone/tablette il faudra donc vous rendre dans les options développeur et activer le débogage USB.

Image non disponible

Si vous utilisez un émulateur, il n'y a rien à faire, tout est prévu par défaut.

À la création d'un projet Android Studio il faut créer également automatiquement une variante débogage de votre application. Elle n'est pas visible dans le fichier gradle, mais elle est bien présente.

III. Lire et écrire des journaux

L'interface principale pour trouver des informations de débogage est le Logcat. C'est une vue d'Android Studio qui vous permet de voir tous les journaux écrits par le système ou les applications.

Il est accessible via les onglets en bas de l'écran ou via le raccourci « Alt+6 »

Image non disponible

III-A. Lire

Beaucoup d'informations s'affichent dans le Logcat , et la plupart d'entre elles ne nous concernent pas. Il faut donc filtrer les messages selon plusieurs critères :

  1. L'appareil concerné ;
  2. Le processus concerné ;
  3. La sévérité du message ;
  4. Un filtre textuel ;
  5. Le périmètre des journaux.
Image non disponible

Le but est de ne voir que les messages concernant l'application que l'on souhaite déboguer et rien d'autre.

Une fois les filtres appliqués, il faut encore comprendre ce que l'on voit. Fort heureusement tous les messages dans le Logcat ont la même forme :

[date] [heure] [PID-TID/package] [sévérité/tag] [message]

Par exemple :

11-13 13:02:50.071 1901-4229/com.google.android.gms V/AuthZen: Handling delegate intent.

Que nous apprend donc ce message ?

Le 13 novembre à 13h02 l'application com.google.android.gms affiche le message « Handling delegate intent. » de sévérité verbose avec le tag (l'étiquette) AuthZen.

Vous remarquerez aussi différentes couleurs de message. Chaque couleur représente une sévérité de message parmi verbose, debug, info, warning et error. Une chose simple à retenir : quand c'est rouge, ce n'est jamais bon signe !

III-B. Écrire

Maintenant que l'on sait lire le Logcat, nous allons profiter de ses avantages pour afficher nos propres messages.

Cette technique est la méthode de débogage la plus simple qui consiste à ajouter des « traces » dans son code pour vérifier une valeur par exemple.

L'écriture dans le Logcat se fait à l'aide de la classe Log (https://developer.android.com/reference/android/util/Log.html)

Elle permet d'écrire un message dans la sévérité voulue. Par exemple pour écrire une info :

Message d'info
Sélectionnez
Log.i(tag,message) ;

pour un message de débogage :

Message de debug
Sélectionnez
Log.d(tag,message) ;

Etc.

Le paramètre tag est une chaîne de caractère libre ; par convention, on la définit généralement au nom de la classe en cours, ce qui permet de savoir rapidement d'où émane un message.

Le paramètre message quant à lui est le message que l'on souhaite afficher.

Donc par exemple le code suivant :

Exemple message
Sélectionnez
int age = 8;
Log.d("MonActivity","Mon age est de "+age);

Va afficher :

Résultat
Sélectionnez
11-16 13:00:38.856 4580-4580/com.demo.debug D/MonActivity: Mon age est de 8.
Image non disponible

IV. Analyser une stack trace

IV-A. Stack trace ?

Maintenant que vous maîtrisez le Logcat , vous devriez être capable d'y trouver ce qu'on appelle une stack trace (pile d'appel en bon français). Une stack trace est un message d'erreur détaillé vous indiquant un problème particulier et de quelle façon il s'est produit. Avec une stack trace vous êtes en théorie capable de savoir quelle ligne de votre code pose problème et comment ce code a été appelé.

IV-B. Analyse

Pour le besoin de cet article, je vais créer volontairement une erreur : j'essaie d'utiliser un widget qui n'existe pas dans le layout chargé par mon activité :

Exemple erreur
Sélectionnez
Button btn = findViewById(R.id.existepas);
btn.setText("Test");

Le résultat est un plantage au niveau de l'application :

Image non disponible

Et inévitablement, on trouve une stack trace dans le Logcat :

11-20 14:27:56.159 4837-4837/com.demo.debug E/AndroidRuntime: FATAL EXCEPTION: main

Process: com.demo.debug, PID: 4837

Stack trace
Sélectionnez
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.demo.debug/com.demo.debug.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setText(java.lang.CharSequence)' on a null object reference
      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
      at android.app.ActivityThread.access$800(ActivityThread.java:151)
      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
      at android.os.Handler.dispatchMessage(Handler.java:102)
      at android.os.Looper.loop(Looper.java:135)
      at android.app.ActivityThread.main(ActivityThread.java:5254)
      at java.lang.reflect.Method.invoke(Native Method)
      at java.lang.reflect.Method.invoke(Method.java:372)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
   Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setText(java.lang.CharSequence)' on a null object reference
      at com.demo.debug.MainActivity.onCreate(MainActivity.java:16)
      at android.app.Activity.performCreate(Activity.java:5990)
      at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387) 
      at android.app.ActivityThread.access$800(ActivityThread.java:151) 
      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303) 
      at android.os.Handler.dispatchMessage(Handler.java:102) 
      at android.os.Looper.loop(Looper.java:135) 
      at android.app.ActivityThread.main(ActivityThread.java:5254) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at java.lang.reflect.Method.invoke(Method.java:372) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 

Il y a énormément d'information dans cette trace :

Nous voyons que nous sommes face à une RuntimeException , et plus précisément que le système n'arrive pas à charger l'activité MainActivity :

 
Sélectionnez
Unable to start activity ComponentInfo{com.demo.debug/com.demo.debug.MainActivity}

On voit juste après que nous sommes face à une java.lang.NullPointerException.

Rien pour le moment qui nous permettrait d'identifier la source du problème…

Sauf que si on descend un peu dans la trace, on va trouver la ligne suivante :

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setText(java.lang.CharSequence)' on a null object reference

La ligne Caused by nous indique quelle est la source l'erreur. Ici une NullPointerException lors de l'appel à setText(), et si on descend encore un peu dans la trace on nous indique même l'endroit exact :

at com.demo.debug.MainActivity.onCreate(MainActivity.java:16)

Vous remarquerez que le texte entre parenthèses est cliquable, ce qui va vous emmener directement au bon endroit dans le code. De plus, si le code concerné est le vôtre, le texte s'affichera en bleu contrairement aux autres fichiers qui seront en gris.

C'est donc la fonction onCreate() de MainActivity qui cause problème, et plus précisément la ligne 16.

Ne reste alors qu'à se rendre dans le code pour voir la ligne en question :

 
Sélectionnez
btn.setText("Test");

On peut donc en déduire que la variable « btn » est null, ce qui cause l'erreur.

Pour supprimer l'erreur, il faut donc corriger ce problème.

Dans notre cas, on a deux solutions, soit faire un test avant d'utiliser la variable btn :

 
Sélectionnez
if(btn!= null)
    btn.setText("Test");

Soit de s'assurer que la vue recherchée par findViewById() existe bien.

IV-C. Les grands classiques

NullPointerException

Certainement l'erreur la plus emblématique de Java, qui signifie que l'on essaie d'utiliser une variable null.

Solution : vérifier la nullité d'une variable avant de l'utiliser.

ActivityNotFoundException

Cette erreur intervient quand on essaie d'appeler une activité via startActivity(), mais que cette activity n'est pas déclarée dans le manifest.

Solution : déclarer l'activity dans le manifest.

ClassCastException

Cette erreur survient quand on essaie de lancer un objet dans une instance d'objet inappropriée. Par exemple lancer un TextView en View est tout à fait valide. En revanche lancer un TextView en Integer va provoquer une erreur.

Solution : toujours lancer dans le bon type.

NetworkOnMainThreadException

Dans Android, il est formellement interdit de créer une requête réseau sur le threadUI. Si vous le faites, vous recevrez cette erreur.

Solution : faire les requêtes réseau dans une asynctask ou dans un thread à part.

Activity Has Leaked Window That Was Originally Added Here

Cette erreur survient très souvent quand on essaie d'afficher un dialogue après avoir quitté l'activité à laquelle il est lié.

Solution : toujours détruire les dialog dans onPause() ou onDestroy().

V. Remerciements

Pour la correction orthographique, Maxy35.

Pour la relecture technique, BeeApps.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2018 Grunk. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.