He estado trabajando en una aplicación de Android que se usa con try/catch
frecuencia para evitar que se bloquee incluso en lugares donde no es necesario. Por ejemplo,
Una vista xml layout
con id = toolbar
se hace referencia como:
// 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) {
}
Este enfoque se utiliza en toda la aplicación. El seguimiento de la pila no se imprime y es muy difícil encontrar qué salió mal. La aplicación se cierra repentinamente sin imprimir ningún rastro de pila.
Le pedí a mi superior que me lo explicara y me dijo:
Esto es para prevenir fallas en la producción.
Estoy totalmente en desacuerdo con eso . Para mí, esta no es la forma de evitar que las aplicaciones se bloqueen. Muestra que el desarrollador no sabe lo que está haciendo y tiene dudas.
¿Es este el enfoque que se utiliza en la industria para evitar fallas en las aplicaciones empresariales?
Si try/catch
es realmente, realmente nuestra necesidad, ¿es posible adjuntar un controlador de excepciones con el hilo de la interfaz de usuario u otros hilos y capturar todo allí? Ese será un mejor enfoque si es posible.
Sí, vacío try/catch
es malo e incluso si imprimimos el seguimiento de la pila o la excepción de registro en el servidor, encapsular bloques de código try/catch
aleatoriamente en toda la aplicación no tiene sentido para mí, por ejemplo, cuando cada función está incluida en un try/catch
.
ACTUALIZAR
Como esta pregunta ha recibido mucha atención y algunas personas la han malinterpretado (tal vez porque no la he redactado claramente), la voy a reformular.
Esto es lo que hacen los desarrolladores aquí
Una función está escrita y probada , puede ser una función pequeña que simplemente inicializa vistas o una función compleja, después de probarla se envuelve alrededor del
try/catch
bloque. Incluso para una función que nunca arrojará ninguna excepción.Esta práctica se utiliza en toda la aplicación. En ocasiones, se imprime el seguimiento de la pila y, en ocasiones, solo aparece un
debug log
mensaje de error aleatorio. Este mensaje de error difiere de un desarrollador a otro.Con este enfoque, la aplicación no se bloquea, pero el comportamiento de la aplicación se vuelve indeterminado. Incluso a veces es difícil seguir lo que salió mal.
La verdadera pregunta que he estado haciendo fue; ¿Es la práctica que se sigue en la industria para prevenir fallas en las aplicaciones empresariales? y no estoy preguntando por try / catch vacío . ¿Es como que a los usuarios les encantan las aplicaciones que no fallan que las aplicaciones que se comportan inesperadamente? Porque realmente se reduce a bloquearlo o presentar al usuario una pantalla en blanco o el comportamiento que el usuario desconoce.
Estoy publicando algunos fragmentos del código real aquí.
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) { } }
No es un duplicado de las mejores prácticas de manejo de excepciones de Android , OP está tratando de detectar la excepción para un propósito diferente al de esta pregunta.
fuente
catch(Exception e){}
- este comentario tenía sentido antes de la edición de la preguntaON ERROR RESUME NEXT
Respuestas:
Por supuesto, siempre hay excepciones a las reglas, pero si necesita una regla general, entonces está en lo correcto; Los bloques de captura vacíos son "absolutamente" una mala práctica.
Echemos un vistazo más de cerca, primero comenzando con su ejemplo específico:
try { View view = findViewById(R.id.toolbar); } catch(Exception e) { }
Entonces, se crea una referencia a algo; y cuando eso falla ... no importa; ¡porque esa referencia no se usa en primer lugar! El código anterior es un ruido de línea absolutamente inútil . ¿O la persona que escribió ese código inicialmente asume que una segunda llamada similar mágicamente ya no arrojaría una excepción?
Tal vez esto estaba destinado a verse así:
try { View view = findViewById(R.id.toolbar); ... and now do something with that view variable ... } catch(Exception e) { }
Pero de nuevo, ¡¿en qué ayuda esto ?! Existen excepciones para comunicar respectivamente propagar situaciones de error dentro de su código. Ignorar los errores rara vez es una buena idea. En realidad, una excepción puede tratarse de la siguiente manera:
En pocas palabras: lo mínimo que puede hacer con una excepción es registrarla / rastrearla; para que cuando vengas más tarde depurando algún problema entiendas "OK, en este momento ocurrió esa excepción".
Y como han señalado otros: también evita la captura de excepción en general (bueno, dependiendo de la capa: puede haber buenas razones para tener alguna captura de excepción , e incluso algunos tipos de errores en el nivel más alto, para asegurarse de que nada se pierde; nunca ).
Finalmente, citemos a Ward Cunningham:
Sabes que estás trabajando con código limpio cuando cada rutina que lees resulta ser más o menos lo que esperabas. Puede llamarlo código hermoso cuando el código también hace que parezca que el lenguaje fue creado para el problema.
Deje que eso se hunda y medite sobre ello. El código limpio no te sorprende. El ejemplo que nos muestra sorprende a todos los que lo miran.
Actualización , con respecto a la actualización sobre la que pregunta el OP
try { do something } catch(Exception e) { print stacktrace }
Misma respuesta: hacer eso "por todas partes" también es una mala práctica. Porque este código también sorprende al lector.
Lo anterior:
Entonces, usando try / catch como "patrón" como se muestra; es como se dijo: todavía no es una buena idea. Y sí, previene caídas; pero conduce a todo tipo de comportamiento "indefinido". Ya sabe, cuando detecta una excepción en lugar de tratarla correctamente ; abres una lata de gusanos; porque puede encontrarse con innumerables errores de seguimiento que luego no comprenderá. Porque consumió el evento "causa raíz" anteriormente; impreso en alguna parte; y ese lugar ahora se ha ido.
fuente
De la documentación de Android :
Vamos a titularlo como -
Formato / párrafo ligeramente modificado de la fuente para esta respuesta.
PD: ¡No tengas miedo de las excepciones! ¡¡¡Ellos son amigos!!!
fuente
Object a; try { a = thing(); } catch (Exception e) {...} try { a.action(); } catch (Exception e) {...}
))Exception
podría ser útil como parte de un sistema de "registro antes de fallar", aunque debería volver a lanzar en ese caso.Pondría esto como un comentario a otra respuesta, pero todavía no tengo la reputación de eso.
Tiene razón al decir que es una mala práctica; de hecho, lo que publicó muestra diferentes tipos de malas prácticas con respecto a las excepciones.
Intentaré explicar todos esos a través de este ejemplo.
try { User user = loadUserFromWeb(); if(user.getCountry().equals("us")) { enableExtraFields(); } fillFields(user); } catch (Exception e) { }
Esto puede fallar de varias formas que deben manejarse de manera diferente.
Soluciones
(1) Primero que nada, fallar en silencio no es algo bueno. Si hay una falla, la aplicación no funcionará. En su lugar, debería haber un intento de resolver el problema o mostrar una advertencia, por ejemplo "No se pudieron cargar los datos del usuario, ¿quizás no está conectado a Internet?". Si la aplicación no está haciendo lo que se supone que debe hacer, es mucho más frustrante para el usuario que si simplemente se cierra.
(4) Si el usuario está incompleto, por ejemplo, el país no se conoce y devuelve nulo. El método equals creará una NullPointerException. Si ese NPE simplemente se lanza y se captura como arriba, no se llamará al método fillFields (usuario), aunque aún podría ejecutarse sin problemas. Puede evitar esto incluyendo comprobaciones nulas, cambiando el orden de ejecución o ajustando el alcance de prueba / captura. (O podría guardar la codificación como esta: "nosotros" .equals (user.getCountry ()), pero tenía que dar un ejemplo). Por supuesto, cualquier otra excepción también evitará que se ejecute fillFields (), pero si no hay un usuario, probablemente no desee que se ejecute de todos modos.
(1, 2, 3) La carga desde la web a menudo arroja una variedad de excepciones, desde IOException hasta la excepción HttpMessageNotReadable e incluso simplemente regresar. Podría ser que el usuario no esté conectado a Internet, podría ser que hubo un cambio en un servidor backend o que no funciona, pero no lo sabe porque detecta (Excepción); en su lugar, debe detectar excepciones específicas. Incluso puedes atrapar a varios de ellos así.
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(); }
Espero que esto lo explique.
Al menos como una solución rápida, lo que podría hacer es enviar un evento a su backend con la excepción. Por ejemplo, a través de firebase o crashlytics. De esa manera, al menos puede ver cosas como (oye, la actividad principal no se carga para el 80% de nuestros usuarios debido a un problema como (4).
fuente
Definitivamente es una mala práctica de programación.
Desde el escenario actual, si hay cientos de
try
catch
este tipo, ni siquiera sabrá dónde ocurre la excepción sin depurar la aplicación, lo cual es una pesadilla si su aplicación está en un entorno de producción.Pero puede incluir un registrador para saber cuándo se lanza una excepción (y por qué). No cambiará su flujo de trabajo normal.
... try { View view = findViewById(R.id.toolbar); }catch(Exception e){ logger.log(Level.SEVERE, "an exception was thrown", e); } ...
fuente
Esta es una mala práctica. Otras respuestas han dicho eso, pero creo que es importante dar un paso atrás y comprender por qué tenemos excepciones en primer lugar.
Cada función tiene una condición posterior : un conjunto de cosas que deben ser verdaderas después de que se ejecute esa función. Por ejemplo, una función que lee de un archivo tiene la condición posterior de que los datos del archivo se leerán del disco y se devolverán. Entonces, se lanza una excepción cuando una función no ha podido satisfacer una de sus condiciones posteriores.
Al ignorar una excepción de una función (o incluso ignorarla de manera efectiva simplemente registrando la excepción), está diciendo que está de acuerdo con que esa función no esté haciendo todo el trabajo que acordó hacer. Esto parece poco probable: si una función no se ejecuta correctamente, no hay garantía de que lo que sigue se ejecute en absoluto. Y si el resto de su código funciona bien, ya sea que una función en particular se complete o no, entonces uno se pregunta por qué tiene esa función en primer lugar.
[Ahora bien, hay ciertos casos en los que
catch
está bien que esté vacío . Por ejemplo, el registro es algo que podría justificar envolver en una captura vacía. Su aplicación probablemente funcionará bien incluso si no se puede escribir parte del registro. Pero esos son casos especiales que tienes que esforzarte mucho para encontrarlos en una aplicación normal.]Entonces, el punto es que esta es una mala práctica porque en realidad no mantiene la aplicación en funcionamiento (la supuesta justificación de este estilo). Quizás técnicamente el sistema operativo no lo ha matado. Pero es poco probable que la aplicación siga funcionando correctamente después de simplemente ignorar una excepción. Y en el peor de los casos, podría estar causando daño (por ejemplo, corrompiendo archivos de usuario, etc.).
fuente
Esto es malo por varias razones:
findViewById
arroja una excepción? Arregle eso (y dígame, porque nunca he visto esto) en lugar de atraparlo.Exception
cuándo podría detectar un tipo específico de excepción.Si una aplicación entra en mal estado, es mucho mejor que se bloquee que que funcione en su estado inutilizable. Cuando uno ve una NPE, uno no debería simplemente poner un cheque nulo y alejarse. El mejor enfoque es averiguar por qué algo es nulo y evitar que sea nulo o (si el nulo termina siendo un estado válido y esperado) comprobar si es nulo. Pero debes entender por qué ocurre el problema en primer lugar.
fuente
He estado desarrollando aplicaciones de Android durante los últimos 4-5 años y nunca usé un try catch para la inicialización de la vista.
Si es una barra de herramientas, hazlo así
por ejemplo: - Para obtener un TextView de una vista (fragmento / diálogo / cualquier vista personalizada)
TextView textview = (TextView) view.findViewById (R.id.viewId);
en lugar de esto
Un objeto de vista tiene un alcance mínimo en comparación con su tipo de vista real.
Nota: - Puede que se bloquee porque se cargó la vista. Pero agregar try catch es una mala práctica.
fuente
View
que pueden ser útiles (comosetVisibility()
), sin que especifique exactamente qué clase se usa. (por ejemplo, en el caso de un diseño, que es susceptible de ser cambiado de vez en cuando)Sí, try / catch se usa para evitar que la aplicación se bloquee, pero ciertamente no es necesario try / catch para obtener una vista de XML como se muestra en su pregunta.
try / catch se usa generalmente al realizar cualquier solicitud http, al analizar cualquier String a URL, crear conexiones URL, etc. y también asegurarse de imprimir el seguimiento de la pila. No imprimirlo no tiene mucho sentido rodearlo con try / catch.
fuente
Como se dijo antes, las excepciones generales no deben detectarse, o al menos solo en algunos lugares centrales (generalmente ubicadas en el código de infraestructura / marco, no en el código de la aplicación). Si se detectan excepciones generales y se registran, la aplicación debe cerrarse posteriormente o, como mínimo, se debe informar al usuario de que la aplicación está potencialmente en un estado inestable y que podrían producirse daños en los datos (si el usuario elige continuar con la ejecución). Porque esto es lo que podría suceder si detecta todo tipo de excepciones (sin memoria para nombrar una) y deja la aplicación en un estado indefinido.
En mi humilde opinión, es peor aceptar las excepciones y arriesgar la integridad de los datos, la pérdida de datos o simplemente dejar la aplicación en un estado indefinido que dejar que la aplicación se bloquee y el usuario sepa que algo salió mal y puede volver a intentarlo. Esto también conducirá a mejores problemas informados (más en la raíz del problema), probablemente menos síntomas diferentes que si sus usuarios comienzan a informar todo tipo de problemas que se originan en un estado de aplicación indefinido.
Después de que se haya implementado un manejo / registro / reporte central de excepciones y un apagado controlado, comience a reescribir el manejo de excepciones para detectar excepciones locales lo más específicas posible. Intenta que el bloque try {} sea lo más breve posible.
fuente
Permítanme agregar mi punto de vista, como un hombre que trabaja en la industria del desarrollo móvil corporativo durante más de una década. Primero, algunos consejos generales sobre excepciones, la mayoría de ellos incluidos en las respuestas anteriores:
Ahora, cuando no está desarrollando una aplicación para usted, sino para una empresa o corporación, es común enfrentar requisitos adicionales sobre este tema:
Entonces, la respuesta corta es que el código que encontró no es un ejemplo de buenas prácticas, ya que es difícil y costoso de mantener sin mejorar la calidad de la aplicación.
fuente
Otra perspectiva, como alguien que escribe software empresarial a diario, si una aplicación tiene un error irrecuperable, quiero que se bloquee. Es deseable estrellarse. Si falla, se registra. Si falla más de unas pocas veces en un corto período de tiempo, recibo un correo electrónico que dice que la aplicación falla y puedo verificar que nuestra aplicación y todos los servicios web que consumimos aún funcionan.
Entonces la pregunta:
Es
try{ someMethod(); }catch(Exception e){}
¿buena práctica? ¡No! Aquí hay algunos puntos:
Lo MÁS importante: esta es una mala experiencia para el cliente. ¿Cómo se supone que voy a saber cuando está sucediendo algo malo? Mis clientes están intentando usar mi aplicación y nada funciona. No pueden consultar su cuenta bancaria, pagar sus facturas, haga lo que haga mi aplicación. Mi aplicación es completamente inútil, pero bueno, ¡al menos no se bloqueó! (Parte de mí cree que este desarrollador "sénior" obtiene puntos brownie por números bajos de fallas, por lo que están jugando con el sistema).
Cuando estoy desarrollando y escribo un código incorrecto, si solo estoy capturando y tragando todas las excepciones en la capa superior, no tengo registro. No tengo nada en mi consola y mi aplicación falla silenciosamente. Por lo que puedo decir, todo parece funcionar bien. Entonces confirmo el código ... Resulta que mi objeto DAO fue nulo todo el tiempo, y los pagos del cliente nunca se actualizaron realmente en la base de datos. ¡Ups! Pero mi aplicación no falló, eso es una ventaja.
Jugando al abogado del diablo, digamos que estaba de acuerdo con atrapar y tragar todas las excepciones. Es extremadamente fácil escribir un controlador de excepciones personalizado en Android. Si realmente solo tiene que detectar todas las excepciones, puede hacerlo en un solo lugar y no salpicar
try/catch
todo el código.Algunos de los desarrolladores con los que he trabajado en el pasado pensaban que las fallas eran malas.
Tengo que asegurarles que queremos que nuestra aplicación falle. No, una aplicación inestable no está bien, pero un bloqueo significa que hicimos algo mal y tenemos que arreglarlo. Cuanto más rápido se bloquee, cuanto antes lo encontremos, más fácil será repararlo. La única otra opción en la que puedo pensar es permitir que el usuario continúe en una sesión rota, lo que yo equiparo con cabrear mi base de usuarios.
fuente
Es una mala práctica usarlo
catch(Exception e){}
porque esencialmente está ignorando el error. Lo que probablemente quieras hacer es algo más como:try { //run code that could crash here } catch (Exception e) { System.out.println(e.getMessage()); }
fuente
Prácticamente usamos tu misma lógica. Úselo
try-catch
para evitar que las aplicaciones de producción se bloqueen.Las excepciones NUNCA deben ignorarse. Es una mala práctica de codificación. Los chicos que mantienen el código tendrán realmente dificultades para localizar la parte del código que generó la excepción si no están registrados.
Usamos
Crashlytics
para registrar las excepciones. El código no se bloqueará (pero se interrumpirán algunas funciones). Pero obtienes el registro de excepciones en el panel deFabric/Crashlytics
. Puede mirar estos registros y corregir las excepciones.try { codeThatCouldRaiseError(); } catch (Exception e) { e.printStackTrace(); Crashlytics.logException(e); }
fuente
Si bien estoy de acuerdo con las otras respuestas, hay una circunstancia que he encontrado repetidamente en la que este motivo es marginalmente tolerable. Supongamos que alguien escribió un poco de código para una clase de la siguiente manera:
private int foo=0; . . . public int getFoo() throws SomeException { return foo; }
En esta circunstancia, el método 'getFoo ()' no puede fallar ; siempre habrá un valor legítimo del campo privado 'foo' que se devolverá. Sin embargo, alguien, probablemente por razones pedantes, decidió que este método debería declararse como potencialmente lanzador de una excepción. Si luego intenta llamar a este método en un contexto, por ejemplo, un controlador de eventos, que no permite que se lance una excepción, básicamente se verá obligado a usar esta construcción (incluso entonces, estoy de acuerdo en que al menos uno debería registrar la excepción solo en caso) . Siempre que tengo que hacer esto, siempre agrego al menos un gran comentario gordo 'ESTO NO PUEDE OCURRIR' junto a la cláusula 'catch'.
fuente