Je travaille sur une application Android qui utilise try/catch
fréquemment pour l'empêcher de tomber en panne, même à des endroits où cela n'est pas nécessaire. Par exemple,
Une vue dans xml layout
avec id = toolbar
est référencée comme suit:
// see new example below, this one is just confusing
// it seems like I am asking about empty try/catch
try {
View view = findViewById(R.id.toolbar);
}
catch(Exception e) {
}
Cette approche est utilisée dans toute l'application. La trace de la pile n’est pas imprimée et il est très difficile de trouver ce qui ne va pas. L'application se ferme soudainement sans imprimer aucune trace de pile.
J'ai demandé à mon supérieur de me l'expliquer et il a dit:
Ceci est pour éviter un crash en production.
_ {Je ne suis absolument pas d'accord avec ça}. Pour moi, ce n'est pas le moyen d'empêcher les applications de se bloquer. Cela montre que le développeur ne} _ sait pas ce qu’il fait et doute.
Est-ce l'approche utilisée dans l'industrie pour empêcher les applications d'entreprise de planter?
Si try/catch
est vraiment, vraiment notre besoin, est-il possible d'attacher un gestionnaire d'exceptions avec un thread d'interface utilisateur ou d'autres threads et de tout intercepter? Ce sera une meilleure approche si possible.
Oui, try/catch
vide est mauvais et même si nous imprimons une trace de pile ou une exception de journal sur le serveur, insérer des blocs de code dans try/catch
de manière aléatoire dans l'ensemble de l'application n'a aucun sens, par exemple. quand chaque fonction est incluse dans un try/catch
.
METTRE À JOUR
Comme cette question a attiré beaucoup d'attention et que certaines personnes l'ont mal interprétée (peut-être parce que je ne l'ai pas formulée clairement), je vais la reformuler.
Voici ce que les développeurs font ici
Une fonction est écrite et testée, il peut s'agir d'une petite fonction qui initialise simplement des vues ou d'une vue complexe, après l'avoir testée, elle est encapsulée dans le bloc try/catch
. Même pour une fonction qui ne lève jamais aucune exception.
Cette pratique est utilisée dans toute l'application. Parfois, la trace de pile est imprimée et parfois juste un debug log
avec un message d'erreur aléatoire. Ce message d'erreur diffère d'un développeur à l'autre.
Avec cette approche, l'application ne plante pas, mais le comportement de l'application devient indéterminé. Même parfois, il est difficile de suivre ce qui a mal tourné.
La vraie question que je posais était: _ {Est-ce la pratique courante dans l'industrie visant à empêcher les applications d'entreprise de se bloquer?} Et je ne pose pas de question à propos de (try/catch vide}. Est-ce que c'est comme, les utilisateurs aiment les applications qui ne plantent pas en panne que les applications qui se comportent de manière inattendue? Parce qu’il s’agit vraiment de le bloquer ou de présenter à l’utilisateur un écran vide ou au comportement que l’utilisateur ignore.
Je publie quelques extraits du code réel ici
private void makeRequestForForgetPassword() {
try {
HashMap<String, Object> params = new HashMap<>();
String email= CurrentUserData.msisdn;
params.put("email", "blabla");
params.put("new_password", password);
NetworkProcess networkProcessForgetStep = new NetworkProcess(
serviceCallListenerForgotPasswordStep, ForgotPassword.this);
networkProcessForgetStep.serviceProcessing(params,
Constants.API_FORGOT_PASSWORD);
} catch (Exception e) {
e.printStackTrace();
}
}
private void languagePopUpDialog(View view) {
try {
PopupWindow popupwindow_obj = popupDisplay();
popupwindow_obj.showAsDropDown(view, -50, 0);
} catch (Exception e) {
e.printStackTrace();
}
}
void reloadActivity() {
try {
onCreateProcess();
} catch (Exception e) {
}
}
Il s’agit de pas dupliquer de la meilleure gestion des exceptions Android pratiques , là OP essaie d’attraper une exception pour un différent but que cette question.
Je mettrais cela comme commentaire dans une autre réponse, mais je n’ai pas encore la réputation de le faire.
Vous avez raison de dire que c'est une mauvaise pratique. En fait, ce que vous avez publié montre différents types de mauvaises pratiques en ce qui concerne les exceptions.
Je vais essayer d'expliquer tous ceux-ci via cet exemple.
try {
User user = loadUserFromWeb();
if(user.getCountry().equals("us")) {
enableExtraFields();
}
fillFields(user);
} catch (Exception e) {
}
Cela peut échouer de plusieurs manières qui devraient être traitées différemment.
Solutions
(1) Tout d’abord, échouer en silence n’est pas une bonne chose. En cas d'échec, l'application ne fonctionnera pas. Au lieu de cela, vous devez essayer de résoudre le problème ou afficher un avertissement, par exemple "Impossible de charger les données utilisateur, peut-être que vous n'êtes pas connecté à Internet?". Si l'application ne fait pas ce qu'elle est censée faire, c'est beaucoup plus frustrant pour un utilisateur que de se fermer tout seul.
(4) Si l'utilisateur est incomplet, par ex. le pays n'est pas connu et renvoie null. La méthode equals créera une NullPointerException. Si ce NPE est simplement lancé et attrapé comme ci-dessus, la méthode fillFields (utilisateur) ne sera pas appelée, même si elle peut toujours être exécutée sans problème. Vous pouvez éviter cela en incluant des contrôles nuls, en modifiant l'ordre d'exécution ou en ajustant la portée try/catch. (Vous pouvez également sauvegarder en utilisant le code suivant: "us" .equals (user.getCountry ()), mais je devais donner un exemple). Bien entendu, toute autre exception empêchera également fillFields () d'être exécutée, mais s'il n'y a pas d'utilisateur, vous ne voudrez probablement pas qu'elle soit exécutée de toute façon.
(1, 2, 3) Le chargement à partir du Web génère souvent une série d'exceptions, d'une exception IOException à une exception HttpMessageNotReadable, voire même à un retour. Il se peut que l'utilisateur ne soit pas connecté à Internet, qu'un serveur dorsal ait été modifié ou qu'il soit en panne, mais vous ne le savez pas car vous interceptez (Exception). Vous devez au contraire intercepter des exceptions spécifiques. Vous pouvez même en prendre plusieurs comme ça
try{
User user = loadUserFromWeb(); //throws NoInternetException, ServerNotAvailableException or returns null if user does not exist
if(user == null) {
throw new UserDoesNotExistException(); //there might be better options to solve this, but it highlights how exceptions can be used.
}
fillFields(user);
if("us".equals(user.getCountry()) {
enableExtraFields();
}
} catch(NoInternetException e){
displayWarning("Your internet conneciton is down :(");
} catch(ServerNotAvailableException e){
displayWarning("Seems like our server is having trouble, try again later.");
} catch(UserDoesNotExistException e){
startCreateUserActivity();
}
J'éspère que ça l'explique.
À tout le moins, comme solution rapide, ce que vous pourriez faire est d’envoyer un événement à votre serveur avec l’exception. Par exemple via firebase ou crashlytics. De cette façon, vous pouvez au moins voir des choses comme (hé, l’activité principale ne se charge pas pour 80% de nos utilisateurs en raison d’un problème comme (4).
C'est définitivement une mauvaise pratique de programmation.
Dans le scénario actuel, s'il existe des centaines de try
catch
comme celle-ci, vous ne saurez même pas où l'exception se produit sans déboguer l'application, ce qui est un cauchemar si votre application est en environnement de production.
Mais vous pouvez inclure un enregistreur afin de savoir quand une exception est levée (et pourquoi). Cela ne changera pas votre flux de travail normal.
...
try {
View view = findViewById(R.id.toolbar);
}catch(Exception e){
logger.log(Level.SEVERE, "an exception was thrown", e);
}
...
C'est une mauvaise pratique. D'autres réponses l'ont dit mais je pense qu'il est important de prendre du recul et de comprendre pourquoi nous avons des exceptions au départ.
Chaque fonction a une post-condition - un ensemble de choses qui doivent toutes être vraies après son exécution. Par exemple, une fonction qui lit un fichier a la condition postérieure que les données du fichier soient lues sur le disque et retournées. Une exception est alors levée lorsqu'une fonction n'a pas été en mesure de satisfaire l'une de ses post-conditions.
En ignorant une exception d'une fonction (ou même en l'ignorant simplement en enregistrant l'exception), vous dites que vous acceptez que cette fonction ne réalise pas tout le travail qu'elle a convenu de faire. Cela semble peu probable - si une fonction ne s'exécute pas correctement, rien ne garantit que ce qui suit sera exécuté du tout. Et si le reste de votre code fonctionne correctement, qu’une fonction particulière soit achevée ou non, on peut se demander pourquoi vous avez cette fonction en premier lieu.
[Il existe maintenant certains cas où catch
es vide est correct. Par exemple, la journalisation est quelque chose que vous pourriez justifier de placer dans une capture vide. Votre application fonctionnera probablement correctement même si une partie de la journalisation ne peut pas être écrite. Mais ce sont des cas spéciaux que vous devez travailler très dur à trouver dans une application normale.]
Le fait est que c'est une mauvaise pratique car cela ne maintient pas votre application en cours d'exécution (la prétendue justification de ce style). Peut-être que techniquement, l'OS ne l'a pas tué. Mais il est peu probable que l'application fonctionne toujours correctement après avoir simplement ignoré une exception. Et dans le pire des cas, il pourrait en fait être dommageable (par exemple, corrompre des fichiers d’utilisateur, etc.).
C'est mauvais pour plusieurs raisons:
findViewById
lève une exception? Corrige ça (et dis-moi, parce que je n'ai jamais vu ça) au lieu d'attraper.Exception
si vous pouviez intercepter un type d'exception spécifique. Si une application se met en mauvais état, il est bien préférable qu'elle se bloque que de continuer à fonctionner dans son état inutilisable. Quand on voit un NPE, il ne faut pas simplement s'en tenir à un contrôle nul et s'en aller. La meilleure approche consiste à déterminer la raison pour laquelle quelque chose est null et à l’empêcher d’être nul, ou à vérifier (si null est un état valide et attendu), vérifier la valeur null. Mais vous devez comprendre pourquoi le problème se pose en premier lieu.
Je développe des applications Android depuis 4 ou 5 ans et je n’ai jamais utilisé de prise d’essai pour l’initialisation de la vue.
Si c'est une barre d'outils comme ça
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
par exemple: - Pour obtenir un TextView à partir d'une vue (fragment/boîte de dialogue/toute vue personnalisée)
TextView textview = (TextView) view.findViewById(R.id.viewId);
TextView textview = (TextView) view.findViewById (R.id.viewId);
au lieu de cela
View view = findViewById(R.id.toolbar);
Un objet de vue a une portée minimale par rapport à son type de vue réel.
Remarque: - Peut-être que cela s'est écrasé parce que la vue a été chargée Mais ajouter try catch est une mauvaise pratique.
Comme indiqué précédemment, les exceptions générales ne doivent pas être capturées, ou du moins uniquement dans des emplacements peu centraux (généralement situés dans le code d'infrastructure/infrastructure, pas dans le code d'application). Si vous attrapez des exceptions générales et vous enregistrez, l'application doit être fermée par la suite ou au moins, l'utilisateur doit être informé que l'application est potentiellement dans un état instable et que les données risquent d'être corrompues (si l'utilisateur choisit de continuer l'exécution). Parce que c'est ce qui pourrait arriver si vous capturez toutes sortes d'exceptions (mémoire insuffisante pour en nommer une) et en laissant l'application dans un état non défini.
À mon humble avis, il est pire d’avaler des exceptions et de compromettre l’intégrité des données, la perte de données ou tout simplement de laisser l’application dans un état non défini que de laisser l’application planter et l’utilisateur sait que quelque chose s’est mal passé et peut essayer à nouveau. Cela entraînera également de meilleurs problèmes signalés (plus à la racine du problème), probablement moins de symptômes différents que si vos utilisateurs commencent à signaler tout type de problèmes liés à un état d'application non défini.
Une fois que la gestion/la journalisation/la génération d’exceptions centralisée et la fermeture contrôlée sont en place, commencez à réécrire la gestion des exceptions pour capturer les exceptions locales aussi spécifiques que possible. Essayez de faire en sorte que le bloc try {} soit le plus court possible.
Une autre perspective, en tant que personne qui écrit un logiciel d’entreprise au quotidien, si une application a une erreur irrécupérable, je veux elle tombe en panne. Crashing est souhaitable. S'il se bloque, il est connecté. S'il se bloque plus d'une fois sur une courte période, je reçois un courrier électronique l'informant que l'application se bloque et je peux vérifier que notre application et tous les services Web que nous consommons fonctionnent toujours.
Alors la question:
Est
try{
someMethod();
}catch(Exception e){}
bonnes pratiques? Non! Voici quelques points:
La chose la plus importante: c'est une mauvaise expérience client. Comment suis-je censé savoir quand quelque chose de mauvais se passe? Mes clients essaient d'utiliser mon application et rien ne fonctionne. Ils ne peuvent pas consulter leur compte bancaire, payer leurs factures, quoi que fasse mon application. Mon application est complètement inutile, mais bon, au moins elle ne s'est pas écrasée! (Une partie de moi pense que ce développeur "senior" obtient des points brownie pour un faible nombre d'accidents, alors ils jouent avec le système.)
Quand je fais du développement et que j'écris du mauvais code, si je ne fais que capturer et avaler toutes les exceptions de la couche supérieure, je n'ai pas de journalisation. Je n'ai rien dans ma console et mon application échoue silencieusement. D'après ce que je peux dire, tout semble bien fonctionner. Donc, je commets le code ... Il s'avère que mon objet DAO était null tout le temps, et que les paiements du client n'étaient jamais réellement mis à jour dans la base de données. Oups! Mais mon application n'a pas planté, c'est donc un avantage.
En défendant les intérêts du diable, disons que je pouvais attraper et avaler toutes les exceptions. Il est extrêmement facile d'écrire un gestionnaire d'exceptions personnalisé sous Android. Si vous voulez vraiment (avoir} _ attraper toutes les exceptions, vous pouvez le faire en un seul endroit et ne pas utiliser le code try/catch
partout dans votre code.
Certains des développeurs avec lesquels j'ai travaillé dans le passé ont pensé que se bloquer était une mauvaise chose.
Je dois leur assurer que nous voulons notre application plante. Non, une application instable n'est pas acceptable, mais un blocage signifie que nous avons commis une erreur et que nous devons y remédier. Plus vite il se bloque, plus tôt nous le trouvons, plus il est facile de le réparer. La seule autre option à laquelle je peux penser est de permettre à l'utilisateur de continuer dans une session interrompue, ce qui équivaut à assommer ma base d'utilisateurs.
Permettez-moi d'ajouter mon point de vue, en tant que personne travaillant dans le secteur du développement mobile d'entreprise pendant plus d'une décennie. Premièrement, quelques conseils généraux sur les exceptions, dont la plupart sont inclus dans les réponses ci-dessus:
Maintenant, lorsque vous ne développez pas une application pour vous-même, mais pour une entreprise ou une entreprise, il est courant de faire face à des exigences supplémentaires à ce sujet:
La réponse courte est donc que le code que vous avez trouvé n'est pas un bon exemple de pratique, car il est difficile et coûteux de le maintenir sans améliorer la qualité de l'application.
Oui, try/catch est utilisé pour éviter le blocage de l'application, mais vous n'avez certainement pas besoin de try/catch pour extraire une vue à partir de XML, comme indiqué dans votre question.
try/catch est généralement utilisé lors de toute requête http, lors de l'analyse syntaxique de String en URL, lors de la création de connexions d'URL, etc., et veille également à imprimer le suivi de la pile. Ne pas l'imprimer n'a pas beaucoup de sens en l'entourant d'essayer/attraper.
C'est une mauvaise pratique d'utiliser catch(Exception e){}
parce que vous ignorez essentiellement l'erreur. Ce que vous voulez probablement faire est quelque chose de plus semblable à:
try {
//run code that could crash here
} catch (Exception e) {
System.out.println(e.getMessage());
}
Nous utilisons beaucoup votre même logique. Utilisez try-catch
pour empêcher les applications de production de tomber en panne.
Les exceptions doivent êtreJAMAISignorées. C'est une mauvaise pratique de codage. Les personnes qui gèrent le code auront beaucoup de difficulté à localiser la partie du code qui a déclenché l'exception si elles ne sont pas connectées.
Nous utilisons Crashlytics
pour consigner les exceptions. Le code ne plantera pas (mais certaines fonctionnalités seront perturbées). Mais vous obtenez le journal des exceptions dans le tableau de bord de Fabric/Crashlytics
. Vous pouvez consulter ces journaux et corriger les exceptions.
try {
codeThatCouldRaiseError();
} catch (Exception e) {
e.printStackTrace();
Crashlytics.logException(e);
}
Bien que je sois d’accord avec les autres réponses, j’ai rencontré à maintes reprises une situation où ce motif est marginalement tolérable. Supposons que quelqu'un écrive un peu de code pour une classe comme suit:
private int foo=0;
. . .
public int getFoo() throws SomeException { return foo; }
Dans ce cas, la méthode 'getFoo ()' ne peut pas échouer - il y aura toujours une valeur légitime du champ privé 'foo' à renvoyer. Pourtant, quelqu'un - probablement pour des raisons pédantes - a décidé que cette méthode devait être déclarée comme pouvant potentiellement générer une exception. Si vous essayez ensuite d'appeler cette méthode dans un contexte - par exemple un gestionnaire d'événements - qui ne permet pas de lever une exception, vous êtes essentiellement obligé d'utiliser cette construction (même dans ce cas, je conviens qu'il faut au moins connectez l'exception juste au cas où). Chaque fois que je dois faire cela, j'ajoute au moins un gros commentaire 'CECI NE PEUT PAS OCCURER' à côté de la clause 'catch'.