5
\$\begingroup\$

The AsyncTask available in android, gives you the result from doInBackground on the UI thread so that you can update UI according to the result, but exceptions need to be handled inside doInBackground function.

So following is my generic solution to the problem, the main objective here is to allow user to handle exceptions exactly the same way he does when working with synchronous code.

public abstract class AsyncTaskWithThrowable<Params, Progress, Result>  {

    public class ResultHolder<Result> {

        private ResultHolder(Result result) {
            _result = result;
        }

        private ResultHolder(Throwable throwable) {
            _throwable = throwable;
        }

        private Throwable _throwable = null;

        private Result _result;

        public Result getResult() throws Throwable{
            if (_throwable != null) {
                throw _throwable;
            }

            return _result;
        }
    }

    private AsyncTask<Params, Progress, ResultHolder<Result>> _asyncTask = new AsyncTask<Params, Progress, ResultHolder<Result>>() {
        @Override
        protected ResultHolder<Result> doInBackground(Params... params)  {

            try {
                return new ResultHolder<Result>(AsyncTaskWithThrowable.this.doInBackground(params));
            }
            catch (Throwable e) {
                new ResultHolder<Result>(e);
                return null;
            }
        }

        @Override
        protected void onPreExecute() {
            AsyncTaskWithThrowable.this.onPreExecute();
        }

        @Override
        protected void onPostExecute(ResultHolder<Result> result) {
            AsyncTaskWithThrowable.this.onPostExecute(result);
        }

        @Override
        protected void onProgressUpdate(Progress... values) {
            AsyncTaskWithThrowable.this.onProgressUpdate(values);
        }
    };

    public abstract Result doInBackground(Params ... params) throws Throwable;

    protected void onPreExecute() {
    }

    @SuppressWarnings({"UnusedDeclaration"})
    protected void onPostExecute(ResultHolder<Result> resultHolder) {
    }

    @SuppressWarnings({"UnusedDeclaration"})
    protected void onProgressUpdate(Progress... values) {
    }

    public final AsyncTaskWithThrowable<Params, Progress, Result> execute(Params... params) {
         _asyncTask.execute(params);
        return this;
    }
}

And this is how you use it.

new AsyncTaskWithThrowable<Boolean, Void, String>(){

    @Override
    protected String doInBackground(Boolean ...params) {
        if (!params[0]) {
            throw new InvalidParameterException();
        }
        return "result";
    }

    @Override
    protected void onPostExecute(ResultHolder<String> resultHolder) {
        try{
            String result = resultHolder.getResult();
            // Change UI
        }
        catch (InvalidParameterException e) {
            // Change UI
        }
        catch (Throwable e) {
            // I don't like this catch block, but i couldn't find a way to avoid it.
        }
    }
}.execute(false);

The problem I see with it is user is forced to handle Throwable (also doInBackground can throw anything), but I cannot use typed parameter for exception here, as Java doesn't allow to catch it.

\$\endgroup\$
2
  • \$\begingroup\$ After having received answers, please do not add revised versions of your code to your question or modify your previous code in such a way that it invalidates answers. See our meta question Can I edit my own question to include revised code? for what you can do after having received answers to your question. \$\endgroup\$ – Simon Forsberg Dec 18 '14 at 13:16
  • \$\begingroup\$ Consider Callable<T> and Future<T> with your own Executor \$\endgroup\$ – Mark Renouf Dec 18 '14 at 21:32
5
\$\begingroup\$

First of all, a few notes:

  • ResultHolder<Result> can be a public static class.

  • This just feels wrong:

    catch (Throwable e) {
        new ResultHolder<Result>(e);
        return null;
    }
    

    You should probably return that ResultHolder, shouldn't you?

  • Avoid @SuppressWarnings({"UnusedDeclaration"}). If you have a warning there, then find out why it is there and solve it. Don't mark it as ignored. Sometimes, marking warnings ignored this way is fine, I would never do it with an UnusedDeclaration warning though.

As for your question about the Throwable, you are right that it is not a good idea to use that there. You can solve this by wrapping the exception in another.

public class AsyncException extends Exception {
    public AsyncException(String message, Throwable cause) {
        super(message, cause);
    }
}

And then:

public abstract Result doInBackground(Params ... params) throws AsyncException;

public Result getResult() throws AsyncException {

private ResultHolder(AsyncException throwable) {

@Override
protected ResultHolder<Result> doInBackground(Params... params)  {
    try {
        return new ResultHolder<Result>(AsyncTaskWithThrowable.this.doInBackground(params));
    }
    catch (Exception e) {
        return new ResultHolder<Result>(new AsyncException("Error while performing doInBackground", e));
    }
}

Problem solved! Note that I changed to catching Exception instead of Throwable as Errors (throwables that are not exceptions) shouldn't really be caught, they are much more serious so they are meant to crash everything.

\$\endgroup\$
2
  • \$\begingroup\$ thanks for the answer, i agree with most of the things you mentioned and have updated my code accordingly. But I have couple of things, first regarding suppressing UnusedDeclaration, i have to do this because i want those methods to have empty body (so the extending class can optionally implement them), and this causes the parameter to be unused. \$\endgroup\$ – Ammar Dec 18 '14 at 12:47
  • \$\begingroup\$ @Ammar OK, then I understand what you're suppressing, I thought the warning was for something else. \$\endgroup\$ – Simon Forsberg Dec 18 '14 at 13:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.