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 returntrue
which is probably only supposed to happen when everything goes according to plan. Addreturn false;
to the catch block. (Yes, thefinally
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
orLog.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)
- I.e., use the three-argument version of the Log methods when dealing with exceptions:
- 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 withMyClass.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 useTAG
for the tag in your log statements.- Although remember your log tags are limited to 23 characters, so keep that in mind.
- 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;}