Using try/catch for preventing app from crashes

前端 未结 14 1842
你的背包
你的背包 2021-01-30 03:37

I have been working on an Android app which uses try/catch frequently to prevent it from crashing even on places where there is no need. For example,

A view

相关标签:
14条回答
  • 2021-01-30 04:38

    I would put this as a comment to some other answer, but I don't have the reputation for that yet.

    You are correct in saying that it's bad practice, in fact what you posted shows different types of bad practice in regards to exceptions.

    1. Lack of error handling
    2. Generic Catch
    3. No intentional exceptions
    4. Blanket Try/catch

    I'll try to explain all of those via this example.

    try {
       User user = loadUserFromWeb();     
       if(user.getCountry().equals("us")) {  
           enableExtraFields();
       }
       fillFields(user);   
    } catch (Exception e) { 
    }
    

    This can fail in several ways that should be handled differently.

    1. The fields will not be filled, so the user is presented with an empty screen and then... what? Nothing - lack of error handling.
    2. There's no distinction between different types of errors, e.g. Internet problems or problems with the server itself (outage, broken request, corrupted transmission, ...) - Generic catch.
    3. You can not use exceptions for your own purposes because the current system interferes with that. - No intentional exceptions
    4. Unessential and unexpected errors (e.g. null.equals(...)) can cause essential code not to execute. - Blanket try/catch

    Solutions

    (1) First of all, failing silently is not a good thing. If there's a failure, the app won't work. Instead there should be an attempt to resolve the problem or a display a warning, for example "Could not load user data, maybe you're not connected to the Internet?". If the app is not doing what it's supposed to, that's way more frustrating for a user than if it just closes itself.

    (4) If the User is incomplete, e.g. the country is not known and returns null. The equals method will create a NullPointerException. If that NPE is just thrown and caught like above, the fillFields(user) method will not be called, even though it could still be executed without problems. You could prevent this by including null checks, changing execution order, or adjusting the try/catch scope. (Or you could do save coding like this: "us".equals(user.getCountry()), but I had to provide an example). Of course any other exception will also prevent fillFields() from being executed, but if there's no user, you probably don't want it executed anyway.

    (1, 2, 3)Loading from web often throws a variety of exceptions, from IOException to HttpMessageNotReadable exception to even just returning. Could be that the user isn't connected to the internet, could be that there was a change to a backend server or it is down, but you don't know because you do catch(Exception) - instead you should catch specific exceptions. You can even catch several of them like this

    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();
    }
    

    I hope that explains it.

    At the very least as a quick fix, what you could do is send an event to your backend with the exception. For example through firebase or crashlytics. That way you can at least see stuff like (hey, the main activity does not load for 80% of our users due to a problem like (4).

    0 讨论(0)
  • 2021-01-30 04:38

    As said before, general exceptions shouldn't be catched, or at least only in a few central places (usually located in framework/infrastructure code, not application code). If catching general exceptions and logging it, the application should be shut down afterwards, or at the very least user should be informed that application is potentially in a unstable state and data corruption could occur (if user chooses to continue execution). Because this is what might happen if you catch all sort of exceptions (out of memory to name one) and leaving the app in an undefined state.

    IMHO it is worse to swallow exceptions and risk data integrity, data loss, or simply leaving the app in an undefined state than letting the app crash and the user knows that something went wrong and can try again. This will also lead to better issues reported (more at the root of the problem), probably fewer different symptoms than if your users start to report all kind of troubles originating from undefined application state.

    After a central exception handling/logging/reporting and controlled shutdown is in place, start rewriting exception handling to catch local exceptions as specific as possible. Try to make the try{} block as short as possible.

    0 讨论(0)
提交回复
热议问题