AsyncTask and error handling on Android

AndroidError HandlingHandlerAndroid Asynctask

Android Problem Overview


I'm converting my code from using Handler to AsyncTask. The latter is great at what it does - asynchronous updates and handling of results in the main UI thread. What's unclear to me is how to handle exceptions if something goes haywire in AsyncTask#doInBackground.

The way I do it is to have an error Handler and send messages to it. It works fine, but is it the "right" approach or is there better alternative?

Also I understand that if I define the error Handler as an Activity field, it should execute in the UI thread. However, sometimes (very unpredictably) I will get an Exception saying that code triggered from Handler#handleMessage is executing on the wrong thread. Should I initialize error Handler in Activity#onCreate instead? Placing runOnUiThread into Handler#handleMessage seems redundant but it executes very reliably.

Android Solutions


Solution 1 - Android

> It works fine but is it the "right" > approach and is there better > alternative?

I hold onto the Throwable or Exception in the AsyncTask instance itself and then do something with it in onPostExecute(), so my error handling has the option of displaying a dialog on-screen.

Solution 2 - Android

Create an AsyncResult object ( which you can also use in other projects)

public class AsyncTaskResult<T> {
	private T result;
	private Exception error;

	public T getResult() {
		return result;
	}

	public Exception getError() {
		return error;
	}

	public AsyncTaskResult(T result) {
		super();
		this.result = result;
	}

	public AsyncTaskResult(Exception error) {
		super();
		this.error = error;
	}
}

Return this object from your AsyncTask doInBackground methods and check it in the postExecute. ( You can use this class as a base class for your other async tasks )

Below is a mockup of a task that gets a JSON response from the web server.

AsyncTask<Object,String,AsyncTaskResult<JSONObject>> jsonLoader = new AsyncTask<Object, String, AsyncTaskResult<JSONObject>>() {

		@Override
		protected AsyncTaskResult<JSONObject> doInBackground(
				Object... params) {
			try {
			    // get your JSONObject from the server
				return new AsyncTaskResult<JSONObject>(your json object);
			} catch ( Exception anyError) {
				return new AsyncTaskResult<JSONObject>(anyError);
			}
		}
		
		protected void onPostExecute(AsyncTaskResult<JSONObject> result) {
			if ( result.getError() != null ) {
				// error handling here
			}  else if ( isCancelled()) {
				// cancel handling here
			} else {
				
				JSONObject realResult = result.getResult();
				// result handling here
			}
		};
		
	}

Solution 3 - Android

When I feel the need to handle Exceptions in AsyncTask properly, I use this as super class:

public abstract class ExceptionAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {

	private Exception exception=null;
	private Params[] params;

	@Override
	final protected Result doInBackground(Params... params) {
		try {
			this.params = params; 
			return doInBackground();
		}
		catch (Exception e) {
			exception = e;
			return null;
		}
	}

	abstract protected Result doInBackground() throws Exception;

	@Override
	final protected void onPostExecute(Result result) {
		super.onPostExecute(result);
		onPostExecute(exception, result);
	}

	abstract protected void onPostExecute(Exception exception, Result result);
	
	public Params[] getParams() {
		return params;
	}

}

As normal, you override doInBackground in your subclass to do background work, happily throwing Exceptions where needed. You are then forced to implement onPostExecute (because it's abstract) and this gently reminds you to handle all types of Exception, which are passed as parameter. In most cases, Exceptions lead to some type of ui output, so onPostExecute is a perfect place to do that.

Solution 4 - Android

If you want to use the RoboGuice framework which brings you other benefits you can try the RoboAsyncTask which has an extra Callback onException(). Works real good and I use it. http://code.google.com/p/roboguice/wiki/RoboAsyncTask

Solution 5 - Android

I made my own AsyncTask subclass with an interface that defines callbacks for success and failure. So if an exception is thrown in your AsyncTask, the onFailure function gets passed the exception, otherwise the onSuccess callback gets passed your result. Why android doesn't have something better available is beyond me.

public class SafeAsyncTask<inBackgroundType, progressType, resultType>
extends AsyncTask<inBackgroundType, progressType, resultType>  {
    protected Exception cancelledForEx = null;
    protected SafeAsyncTaskInterface callbackInterface;

    public interface SafeAsyncTaskInterface <cbInBackgroundType, cbResultType> {
        public Object backgroundTask(cbInBackgroundType[] params) throws Exception;
        public void onCancel(cbResultType result);
        public void onFailure(Exception ex);
        public void onSuccess(cbResultType result);
    }

    @Override
    protected void onPreExecute() {
        this.callbackInterface = (SafeAsyncTaskInterface) this;
    }

    @Override
    protected resultType doInBackground(inBackgroundType... params) {
        try {
            return (resultType) this.callbackInterface.backgroundTask(params);
        } catch (Exception ex) {
            this.cancelledForEx = ex;
            this.cancel(false);
            return null;
        }
    }

    @Override
    protected void onCancelled(resultType result) {
        if(this.cancelledForEx != null) {
            this.callbackInterface.onFailure(this.cancelledForEx);
        } else {
            this.callbackInterface.onCancel(result);
        }
    }

    @Override
    protected void onPostExecute(resultType result) {
        this.callbackInterface.onSuccess(result);
    }
}

Solution 6 - Android

A more comprehensive solution to [Cagatay Kalan][1]'s solution is shown below:

AsyncTaskResult

public class AsyncTaskResult<T> 
{
    private T result;
    private Exception error;

    public T getResult() 
    {
        return result;
    }

    public Exception getError() 
    {
        return error;
    }

    public AsyncTaskResult(T result) 
    {
        super();
        this.result = result;
    }

    public AsyncTaskResult(Exception error) {
        super();
        this.error = error;
    }
}

ExceptionHandlingAsyncTask

public abstract class ExceptionHandlingAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, AsyncTaskResult<Result>>
{
	private Context	context;

	public ExceptionHandlingAsyncTask(Context context)
	{
		this.context = context;
	}
	
	public Context getContext()
	{
		return context;
	}

	@Override
	protected AsyncTaskResult<Result> doInBackground(Params... params)
	{
		try
		{
			return new AsyncTaskResult<Result>(doInBackground2(params));
		}
		catch (Exception e)
		{
			return new AsyncTaskResult<Result>(e);
		}
	}

	@Override
	protected void onPostExecute(AsyncTaskResult<Result> result)
	{
		if (result.getError() != null)
		{
            onPostException(result.getError());
		}
		else
		{
			onPostExecute2(result.getResult());
		}
		super.onPostExecute(result);
	}

	protected abstract Result doInBackground2(Params... params);

	protected abstract void onPostExecute2(Result result);
	
	protected void onPostException(Exception exception)
	{
						new AlertDialog.Builder(context).setTitle(R.string.dialog_title_generic_error).setMessage(exception.getMessage())
				.setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener()
				{
					public void onClick(DialogInterface dialog, int which)
					{
						//Nothing to do
					}
				}).show();
	}
}

Example Task

public class ExampleTask extends ExceptionHandlingAsyncTask<String, Void, Result>
{
	private ProgressDialog	dialog;
	
	public ExampleTask(Context ctx)
	{
		super(ctx);
		dialog = new ProgressDialog(ctx);
	}

	@Override
	protected void onPreExecute()
	{
		dialog.setMessage(getResources().getString(R.string.dialog_logging_in));
		dialog.show();
	}
	
	@Override
	protected Result doInBackground2(String... params)
	{
		return new Result();
	}

	@Override
	protected void onPostExecute2(Result result)
	{
		if (dialog.isShowing())
			dialog.dismiss();
		//handle result
	}
	
	@Override
	protected void onPostException(Exception exception)
	{
		if (dialog.isShowing())
			dialog.dismiss();
        super.onPostException(exception);
	}
}

[1]: https://stackoverflow.com/a/6312491/581401 "Cagatay Kalan"

Solution 7 - Android

Another way that doesn't depend on variable member sharing is to use cancel.

This is from android docs:

> public final boolean cancel (boolean mayInterruptIfRunning) > > Attempts to cancel execution of this task. This > attempt will fail if the task has already completed, already been > cancelled, or could not be cancelled for some other reason. If > successful, and this task has not started when cancel is called, this > task should never run. If the task has already started, then the > mayInterruptIfRunning parameter determines whether the thread > executing this task should be interrupted in an attempt to stop the > task. > > Calling this method will result in onCancelled(Object) being invoked > on the UI thread after doInBackground(Object[]) returns. Calling this > method guarantees that onPostExecute(Object) is never invoked. After > invoking this method, you should check the value returned by > isCancelled() periodically from doInBackground(Object[]) to finish the > task as early as possible.

So you can call cancel in catch statement and be sure that onPostExcute is never called, but instead onCancelled is invoked on UI thread. So you can show the error message.

Solution 8 - Android

This simple class can help you

public abstract class ExceptionAsyncTask<Param, Progress, Result, Except extends Throwable> extends AsyncTask<Param, Progress, Result> {
	private Except thrown;
	
	@SuppressWarnings("unchecked")
	@Override
	/**
	 * Do not override this method, override doInBackgroundWithException instead
	 */
	protected Result doInBackground(Param... params) {
		Result res = null;
		try {
			res = doInBackgroundWithException(params);
		} catch (Throwable e) {
			thrown = (Except) e;
		}
		return res;
	}

	protected abstract Result doInBackgroundWithException(Param... params) throws Except;
	
	@Override
	/**
	 * Don not override this method, override void onPostExecute(Result result, Except exception) instead
	 */
	protected void onPostExecute(Result result) {
		onPostExecute(result, thrown);
		super.onPostExecute(result);
	}
	
	protected abstract void onPostExecute(Result result, Except exception);
}

Solution 9 - Android

Actually, AsyncTask use FutureTask & Executor, FutureTask support exception-chain

First let's define a helper class

public static class AsyncFutureTask<T> extends FutureTask<T> {

    public AsyncFutureTask(@NonNull Callable<T> callable) {
        super(callable);
    }

    public AsyncFutureTask<T> execute(@NonNull Executor executor) {
        executor.execute(this);
        return this;
    }

    public AsyncFutureTask<T> execute() {
        return execute(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    @Override
    protected void done() {
        super.done();
        //work done, complete or abort or any exception happen
    }
}

Second, let's use

    try {
        Log.d(TAG, new AsyncFutureTask<String>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                //throw Exception in worker thread
                throw new Exception("TEST");
            }
        }).execute().get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        //catch the exception throw by worker thread in main thread
        e.printStackTrace();
    }

or use FutureTask directly like below

    FutureTask<?> futureTask = new FutureTask(() -> {throw new RuntimeException("Exception in TaskRunnable");}) {
        @Override
        protected void done() {
            super.done();
            //do something
            Log.d(TAG,"FutureTask done");
        }
    };

    AsyncTask.THREAD_POOL_EXECUTOR.execute(futureTask);

    try {
        futureTask.get();
    } catch (ExecutionException | InterruptedException e) {
        Log.d(TAG, "Detect exception in futureTask", e);
    }

logcat as below enter image description here

Solution 10 - Android

Personally, I will use this approach. You can just catch the exceptions and print out the stack trace if you need the info.

make your task in background return a boolean value.

it's like this:

    @Override
        		protected Boolean doInBackground(String... params) {
        			return readXmlFromWeb(params[0]);
         }
        
        @Override
        		protected void onPostExecute(Boolean result) {
    
              if(result){
              // no error
               }
              else{
                // error handling
               }
}

Solution 11 - Android

If you know the correct exception then you can call the

Exception e = null;

publishProgress(int ...);

eg:

@Override
protected Object doInBackground(final String... params) {

    // TODO Auto-generated method stub
    try {
        return mClient.call(params[0], params[1]);
    } catch(final XMLRPCException e) {

        // TODO Auto-generated catch block
        this.e = e;
        publishProgress(0);
        return null;
    }
}

and go to "onProgressUpdate" and do the folowing

@Override
protected void onProgressUpdate(final Integer... values) {

    // TODO Auto-generated method stub
    super.onProgressUpdate(values);
    mDialog.dismiss();
    OptionPane.showMessage(mActivity, "Connection error", e.getMessage());
}

This will be helpful in some cases only. Also you can keep a Global Exception variable and access the exception.

Solution 12 - Android

Another possibility would be to use Object as return type, and in onPostExecute() check for the object type. It is short.

class MyAsyncTask extends AsyncTask<MyInObject, Void, Object> {

    @Override
    protected AsyncTaskResult<JSONObject> doInBackground(MyInObject... myInObjects) {
        try {
            MyOutObject result;
            // ... do something that produces the result
            return result;
        } catch (Exception e) {
            return e;
        }
    }

    protected void onPostExecute(AsyncTaskResult<JSONObject> outcome) {
        if (outcome instanceof MyOutObject) {
            MyOutObject result = (MyOutObject) outcome;
            // use the result
        } else if (outcome instanceof Exception) {
            Exception e = (Exception) outcome;
            // show error message
        } else throw new IllegalStateException();
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBostoneView Question on Stackoverflow
Solution 1 - AndroidCommonsWareView Answer on Stackoverflow
Solution 2 - AndroidCagatay KalanView Answer on Stackoverflow
Solution 3 - AndroidsulaiView Answer on Stackoverflow
Solution 4 - AndroidludwigmView Answer on Stackoverflow
Solution 5 - AndroidErlVoltonView Answer on Stackoverflow
Solution 6 - AndroidvahaptView Answer on Stackoverflow
Solution 7 - AndroidAliView Answer on Stackoverflow
Solution 8 - AndroidDenisView Answer on Stackoverflow
Solution 9 - AndroidYessyView Answer on Stackoverflow
Solution 10 - AndroidHarryView Answer on Stackoverflow
Solution 11 - AndroidAjmal Muhammad PView Answer on Stackoverflow
Solution 12 - AndroidMatthias RongeView Answer on Stackoverflow