11

I have an AsyncTask to do SQLite database migration in the background (create or upgrade). Let's say somehow an IOException or SQLiteException is thrown inside doInBackground and it's pointless for the app to continue running because database state might be not in desired state. I'm kind of confused on what to do in this situation.

I'm thinking about letting the application crash as soon as possible and show dialog with error message, but I'm not really sure how to this inside doInBackground, because:

  1. This function is not executed in UI thread so I don't know if I can show a dialog.
  2. I don't know how to access current activity within AsyncTask, so I can't finish() it.
  3. I want to somehow throw the exception to the upper layer and let an activity handle it, but it's not possible because doInBackground doesn't list IOException as a checked exception.

Anyone has an advice on how to gracefully handle this situation?

Andree
  • 3,033
  • 6
  • 36
  • 56

3 Answers3

19

You can't show dialog in non-ui thread. You can pass activity reference to async task. To handle this situation you can try catch the exception in doInBackground and re-throw it in onPostExecute

e.g.

private class MyAsyncTaskTask extends AsyncTask<...> {

     private Activity ownerActivity;
     private Exception exceptionToBeThrown;

     public MyAsyncTaskTask(Activity activity) {
         // keep activity reference
         this.ownerActivity = activity;
     }

     protected Long doInBackground(...) {
         try {
             ...
         } catch (Exception e) {
             // save exception and re-thrown it then. 
             exceptionToBeThrown = e;
         }
     }

     protected void onPostExecute(...) {
         // Check if exception exists.
         if (exceptionToBeThrown != null) {
             ownerActivity.handleXXX();
             throw exceptionToBeThrown;
         }
     }
 }

If you async task is in Acvitiy class, then you can directly access it, e.g.,

public class MyActivity extends Activity {
    ...
    AsyncTask<...> task = new AsyncTask<...>() {
        public void onPostExecute(...) {
            // Access activity directly
            MyActivity.this.xxx()
        }
    }
}
Qiang Jin
  • 4,427
  • 19
  • 16
  • 1
    Is it a good practice to call activities method from async task? What happens when `MyActivity` is dead and `AsyncTask` calls `MyActivity.this.xxx()`? – gkiko Jun 05 '14 at 15:39
  • Maybe this will solve the problem http://stackoverflow.com/questions/2735102/ideal-way-to-cancel-an-executing-asynctask – gkiko Jun 05 '14 at 15:52
  • @gkiko It's quite common to invoke activity methods. The result of the AsyncTask needs to be processed, and normally you need to feed the data to the views, which means you need to touch the UI thread. And that should be appropriate to do so. – Qiang Jin Jun 07 '14 at 08:25
  • It's not a good idea attach a AsyncTask to the Activity lifecycle. It could trigger in memory leaks and exceptions (these last posibly silent by the OS, but they're there). When the doInBackground method finish (in the background thread), the onPostExecute method it's invoked, but the the outer class state in that moment could be unexpected. https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html – Dani Apr 01 '17 at 02:39
0

return a unique string everytime such an exception occurs in doInBackground to onPostExecute. and in onPostExecute display a AlertDialog showing appropriate message and ask to try again or something.

SKK
  • 5,261
  • 3
  • 27
  • 39
0

My approach to handle it.

/**
 * Created by Daniel on 02/04/2016.
 */
public abstract class AsyncTaskEnhanced<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {

    // Avoids cyclic calls.
    private boolean onPostExecuteCalled = false;
    private Exception exception;

    @Override
    protected final Result doInBackground(Params... params) {

        try {

            return this.doInBackgroundWithFaultTolerance(params);
        } catch (Exception exception) {

            this.exception = nre;
        }

        return null;
    }

    @Override
    protected final void onPostExecute(Result result) {

        if (this.onPostExecuteCalled) return;

        this.onPostExecuteCalled = true;

        super.onPostExecute(result);

        this.onPostExecuteWithFaultTolerance(result, this.exception);
    }

    protected abstract Result doInBackgroundWithFaultTolerance(Params... params) throws Exception;

    protected abstract void onPostExecuteWithFaultTolerance(Result result, Exception ex);

}
Dani
  • 4,001
  • 7
  • 36
  • 60