Home » Android » android – Catch Exception of AsyncTask. Need Thinking

android – Catch Exception of AsyncTask. Need Thinking

Posted by: admin June 15, 2020 Leave a comment

Questions:

I want to catch exception of a thread in doInBackground and print the error message in onPostExcecute. The problem is I don’t have the Throwable object in onPostExecute. How to catch Exception in non-UI thread and print the error message in UI-thread?

public class TestTask extends AsyncTask<Void, Void, List<String>> {

    @Override
    protected List<String> doInBackground(final Void... params) {
        try {
            ...
            return listOfString;
        } catch(SomeCustomException e) {
            ...
            return null;
        }       
    }

    @Override
    protected void onPostExecute(final List<String> result) {
        if(result == null) {
            // print the error of the Throwable "e".
            // The problem is I don't have the Throwable object here! So I can't check the type of exception.
        }

    }
}

Update after Arun’s answer:

This is my AsyncTask wrapper class. It intends to do handling Exception in doInBackground but I can’t find a good solution to do it.

public abstract class AbstractWorkerTask<Params, Progress, Result>
extends AsyncTask<Params, Progress, Result>
implements Workable {
    protected OnPreExecuteListener onPreExecuteListener;
    protected OnPostExecuteListener<Result> onPostExecuteListener;
    protected ExceptionHappenedListener exceptionHappendedListener;
    private boolean working;

    @Override
    protected void onPreExecute() {
        if (onPreExecuteListener != null) {
            onPreExecuteListener.onPreExecute();
        }
        working = true;
    }

    @Override
    protected void onPostExecute(final Result result) {
        working = false;
        if(/* .........*/ ) {
            exceptionHappendedListener.exceptionHappended(e);
        }
        if (onPostExecuteListener != null) {
            onPostExecuteListener.onPostExecute(result);
        }
    }

    @Override
    public boolean isWorking() {
        return working;
    }

    public void setOnPreExecuteListener(final OnPreExecuteListener onPreExecuteListener) {
        this.onPreExecuteListener = onPreExecuteListener;
    }

    public void setOnPostExecuteListener(final OnPostExecuteListener<Result> onPostExecuteListener) {
        this.onPostExecuteListener = onPostExecuteListener;
    }

    public void setExceptionHappendedListener(final ExceptionHappenedListener exceptionHappendedListener) {
        this.exceptionHappendedListener = exceptionHappendedListener;
    }

    public interface OnPreExecuteListener {
        void onPreExecute();
    }

    public interface OnPostExecuteListener<Result> {
        void onPostExecute(final Result result);
    }

    public interface ExceptionHappenedListener {
        void exceptionHappended(Exception e);
    }
}
How to&Answers:

Change the return type of doInBackground() to Object and when you receive the result in onPostExecute(Object result) use the instanceOf operator to check if the returned result is an Exception or the List<String>.

Edit

Since the result can either be an Exception or else the proper List, you can use the following:

protected void onPostExecute(final Object result) {
    working = false;
    if(result instanceof SomeCustomException) {
        exceptionHappendedListener.exceptionHappended(result);
    }
    else{
        if (onPostExecuteListener != null) {
            onPostExecuteListener.onPostExecute(result);
        }
    }
}

Also change the following statement:

public abstract class AbstractWorkerTask<Params, Progress, Object> extends AsyncTask<Params, Progress, Object>

Answer:

Just store the Exception into a list and handle it later, as onPostExecute() is always called after doInBackground():

public class TestTask extends AsyncTask<Params, Progress, Result> {

  List<Exception> exceptions = new ArrayList<Exception>();

  @Override
  protected Result doInBackground(Params... params) {
    try {
      ...
    } catch(SomeCustomException e) {
      exceptions.add(e);
    }
    return result;
  }

  @Override
  protected void onPostExecute(Result result) {
    for (Exception e : exceptions) {
      // Do whatever you want for the exception here
      ...
    }
  }

}

This is doable but rarely used, as in most situation, we want handle the exception as soon as it get thrown and catched:

public class TestTask extends AsyncTask<Params, Progress, Result> {

  @Override
  protected Result doInBackground(Params... params) {
    try {
      ...
    } catch(SomeCustomException e) {
      // If you need update UI, simply do this:
      runOnUiThread(new Runnable() {
        public void run() {
          // update your UI component here.
          myTextView.setText("Exception!!!");
        }
      });
    }
    return result;
  }

}

Hope this make sense.

Answer:

Changing the return type of doInBackground to Object to possibly pass an Exception and then use instanceof() is a source of code smell (bad programming practice). It is always preferable to restrict your return type to the very specific thing you want returned.

Based on this answer simply add a private member to store the exception thrown in doInBackground and then check for it first thing in onPostExecute.

Only one Exception need be caught because you should stop the actions in doInBackground immediately once the exception is thrown and handle it gracefully in onPostExecute where you have access to the UI elements and so can inform the user of the mishap.

Generic example (body of the AsyncTask):

private Exception mException

@Override
protected Result doInBackground(Params... params) {
    try {
          // --- Do something --- //
    }
    catch( SomeException e ){ mException = e; return null; }
}

@Override
protected void onPostExecute(Result result) {
    if (mException != null) {
        // --- handle exception --- //
        return;
    }

    // --- Perform normal post execution actions --- //
}