package com.hdweiss.morgand.utils; import android.content.Context; import android.os.AsyncTask; import android.util.Log; import android.widget.Toast; /** * Wrapper for {@link android.os.AsyncTask} that allows exceptions to be caught and presented to the * user in a meaningful way. * <p/> * It is required to override {@link #safeDoInBackground(Object[])}, which will be executed within * {@link android.os.AsyncTask#execute(Object[])}. If an exception is caught it will be sent to * {@link #onCancelled(Object)}, which will extract a meaningful message from an exception. The * message will be reported to the user with {@link #reportError(String)}, which will either display a toast, log the error * message to Logcat or do nothing. The reporting behaviour is controlled by {@link #mode}. * <p/> * When an exception is caught {@link #onError()} will be called, otherwise {@link * #onSuccess(Object)} is called. Finally {@link #onCleanup()} will always be called. * <p/> */ public abstract class SafeAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { public enum ReportMode { Toast, Log, Silent } final private ReportMode mode; final protected Context context; protected Exception exception = null; /** * Main worker method. It is run within a {@link android.os.AsyncTask#doInBackground(Object[])}. * Any exception this method throws is caught and reported according to the given {@link * #mode}. */ abstract protected Result safeDoInBackground(Params... params) throws Exception; /** Run after {@link #safeDoInBackground(Object[])} doesn't throw exception. */ protected void onSuccess(Result result) {} /** Run after {@link #safeDoInBackground(Object[])} throws an exception. */ protected void onError() {} /** Guaranteed to run after either {@link #onSuccess(Object)} or {@link #onError()} is run. */ protected void onCleanup() {} public SafeAsyncTask(Context context, ReportMode mode) { this.context = context; this.mode = mode; } @Override final protected Result doInBackground(Params... params) { try { return safeDoInBackground(params); } catch (Exception e) { exception = e; cancel(true); return null; } } @Override protected void onPostExecute(Result result) { super.onPostExecute(result); onSuccess(result); onCleanup(); } @Override final protected void onCancelled() { if (exception != null) { if (exception instanceof ReportableException) reportError(exception.getLocalizedMessage()); else reportError(exception.getLocalizedMessage()); Log.e("SafeAsyncTask", "safeDoInBackground() threw exception", exception); } onError(); onCleanup(); } @Override final protected void onCancelled(Result result) { onCancelled(); } /** Reports the error according to {@link #mode}. */ protected void reportError(String error) { if (error == null || error.isEmpty()) return; if (mode == ReportMode.Toast && context != null) Toast.makeText(context, error, Toast.LENGTH_LONG).show(); if (mode != ReportMode.Silent) Log.d("SafeAsyncTask", error); } public static class ReportableException extends Exception { public ReportableException(String message) { super(message); } } }