8

Folks,

I catch unhandled Android exceptions via a code snippet like this, at the top of onCreate:

    try {
        File crashLogDirectory = new File(Environment.getExternalStorageDirectory().getCanonicalPath() + Constants.CrashLogDirectory);
        crashLogDirectory.mkdirs();

        Thread.setDefaultUncaughtExceptionHandler(new RemoteUploadExceptionHandler(
                this, crashLogDirectory.getCanonicalPath()));
    } catch (Exception e) {
        if (MyActivity.WARN) Log.e(ScruffActivity.TAG, "Exception setting up exception handler! " + e.toString());
    }

I'd like to come up with something similar for about two dozen AsyncTasks I use in my android application, so unhandled exceptions that occur in doInBackground are caught & logged.

Problem is, because AsyncTask takes arbitrary type initializers, I am not sure how to declare a superclass, from which all my AsyncTasks inherit, that sets up this unhandled exception handler.

Can anyone recommend a good design pattern for handling unhandled exceptions in the doInBackground method of AsyncTask that does not involve copy-and-paste of code like that above for every new AsyncTask definition?

Thanks!

UPDATE

Here is the design pattern I used, after looking more closely at the source of AsyncTask

import java.io.File;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;

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

    protected void setupUnhandledExceptionLogging(Context context) {
        try {
            File crashLogDirectory = new File(Environment.getExternalStorageDirectory().getCanonicalPath() + Constants.CrashLogDirectory);
            crashLogDirectory.mkdirs();

            Thread.setDefaultUncaughtExceptionHandler(new RemoteUploadExceptionHandler(
                    context, crashLogDirectory.getCanonicalPath()));
        } catch (Exception e) {
            if (MyActivity.WARN) Log.e(ScruffActivity.TAG, "Exception setting up exception handler! " + e.toString());
        }

    }
}

Then I define my tasks as follows:

private class MyTask extends LoggingAsyncTask<Void, Void, HashMap<String, Object>> {
    protected HashMap<String, Object> doInBackground(Void... args) {
        this.setupUnhandledExceptionLogging(MyActivity.this.mContext);
        // do work
        return myHashMap;
  }
}

Obviously your task can take whatever params necessary with this pattern. It's up to you to define RemoteUploadExceptionHandler to do the necessary logging/uploading.

esilver
  • 27,713
  • 23
  • 122
  • 168

2 Answers2

3

I wouldn't go as far as to call it a design pattern, but just wrap doInBackground() and initialize and/or catch exceptions as necessary.

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

    protected Exception error;

    protected Result doInBackground(Params... params) {
        try {
            init();

            return doRealWork(params);
        } catch (Exception e) {
            error = e;

            Log.e("TAG", e.getMessage(), e);

            return null;
        }
    }

    protected abstract void init();

    protected abstract Result doRealWork(Params... params);
}
Nikolay Elenkov
  • 52,576
  • 10
  • 84
  • 84
  • 1
    Argh, I was just about to hit submit! +1 – Jason LeBrun Aug 11 '11 at 06:01
  • Would this work for any arbitrary three-type combination of AsyncTasks? Or do I have to define custom AsyncTaskWrappers for each type of AsyncTask? What I would like is one general-purpose solution.... – esilver Aug 11 '11 at 06:07
  • Well, try it out. As you can see, it's a parameterized class, just as AsyncTask. You can use it in the same way as AsyncTask, just derive your task classes from this class instead of AsyncTask. – Nikolay Elenkov Aug 11 '11 at 06:19
  • Come to think about it, AsyncTask should have included an `onError(Exception)` callback as well. – Nikolay Elenkov Aug 11 '11 at 06:20
  • This was it; I didn't realize were valid arguments until I looked at the AsyncTask source. I've posted my design pattern above. – esilver Aug 11 '11 at 21:14
0

esilver I trap for exceptions and return a BoolString object, a no throws pattern

    //a utility class to signal success or failure, return an error message, and return a useful String value
//see Try Out in C#
public final class BoolString {
 public final boolean success;
 public final String err;
 public final String value;

 public BoolString(boolean success, String err, String value){
     this.success= success;
     this.err= err;
     this.value= value;
 }
}

USAGE:

private class MyAsynch extends AsyncTask<String, Void, BoolString>{
    protected BoolString doInBackground(String...strings) { // <== DO NOT TOUCH THE UI VIEW HERE      
        return model.tryMyMethod(...); // <== return value BoolString result is sent to onPostExecute
    }        

protected void onPostExecute(BoolString result){
            progress.dismiss();
            if (result.success){
                ... continue with result.value
            }
            else {
                ..log result.err
            }
        }

// NO THROWS VERSION Helper method
public BoolString tryMyMethod(...) {
    try {
        String value= MyMethod(...);
        return new BoolString(true,"",value);
    }
    catch (Exception e){
        return new BoolString(false,e.getMessage(),"");
    }
}
JAL
  • 3,319
  • 2
  • 20
  • 17