4 min read

How To Handle Exceptions Properly

Exceptions are useful if you use them properly. If you don’t use them properly, you will be ridiculed and write bad code. We’ll demonstrate how to handle exceptions correctly by showing some ways NOT to handle exceptions and how you can improve your exception handling.

 

Naive Exception Handling

private Boolean writeImageFileFromUri(Uri uri) {    InputStream is = null;    OutputStream os = null;    try {        is = getActivity().getContentResolver().openInputStream(uri);        Bitmap bitmap = BitmapFactory.decodeStream(is, null, null);        File file = new File(...);        os = new FileOutputStream(file);        bitmap.compress(Bitmap.CompressFormat.PNG, 0, os);        is.close();        os.close();    } catch (Exception e) {        Log.d("Log:", "File failed.");    } finally {        try {            if (is != null) {                is.close();            }            if (os != null) {                os.close();            }        } catch (Exception e) {            Log.d("Log", "Streams did not close");            return false;        }    }    return true;}

 

What’s wrong

This code is ok coming from a beginner to Android programming, but could be improved in a few ways:

  • If an exception is thrown in the main try block, this method may return true which is probably only supposed to happen when everything goes according to plan. Add return false; to the catch block. (Yes, the finally block will be executed before the return.)
  • It would be nice to have some log output related to the exceptions that are thrown. The exception that got thrown can be included in the call to Log.d or Log.e, etc. and will be output in addition to the message that you specify in the common message parameter.
    • I.e., use the three-argument version of the Log methods when dealing with exceptions: Log.e(String, String, Throwable)
  • While we’re at it, let’s include the message the exception contains in our message – frequently that message contains something it would be nice to know but it isn’t output with the stacktrace of the exception.
  • Relatedly, the tag of "Log" is pretty weak and something better should be used. A tag for each class is a convenient constant to have when it comes to logging: public static final String TAG. Set it equal to the name of the class the statement resides in. This is easily done with MyClass.class.getSimpleName(). In this way, if you refactor the name of the class, you won’t need to modify this statement – your IDE will take care of that for you via the refactoring feature. Now just use TAG for the tag in your log statements.
  • Finally, in these catch blocks where we’ll deviate from the expected flow of this method and return false, I’d change the log level to e since an error occurred and this is not just debugging output. If these exceptions are caught in production, you’ll want to have as much information about them as possible from a potential crash report.

Better Code

public static final String TAG = WhateverThisClassIs.class.getSimpleName(); private Boolean writeImageFileFromUri(Uri uri) {    InputStream is = null;    OutputStream os = null;    try {        is = getActivity().getContentResolver().openInputStream(uri);        Bitmap bitmap = BitmapFactory.decodeStream(is, null, null);        File file = new File(...);        os = new FileOutputStream(file);        bitmap.compress(Bitmap.CompressFormat.PNG, 0, os);        // moved closing the streams to the finally block    } catch (Exception e) {        /* This exception could have been caused by a number of things.         * Most likely a failure decoding the bitmap, so we'll keep it a generic Exception         */        Log.e(TAG, "Could not write image file: " + e.getMessage(), e);        return false;    } finally {        try {            if (is != null) {                is.close();            }            if (os != null) {                os.close();            }        } catch (IOException e) {            /* This one could only be caused by a failure to close one of the streams (IOException),             * so let's change the type and output the exception's stack trace in the log             */            Log.e(TAG, "Could not close a stream: " + e.getMessage(), e);            return false;        }    }    return true;}