views:

62

answers:

1

I came across this android example that runs an AsyncTask from a UI thread. The class ExportDatabaseTask is declared and instantiated in the Activity, and apparently it is possible to reference the activity's UI context from the onPreExecute and onPostExecute events, like this:

public class ManageData extends Activity { 

  private ExportDatabaseTask exportDatabaseTask; 
  [...]

  @Override 
  public void onCreate(final Bundle savedInstanceState) { 
    [...]
    ManageData.this.exportDatabaseTask = new ExportDatabaseTask(); 
    ManageData.this.exportDatabaseTask.execute(); 
    [...]
  } 

  private class ExportDatabaseTask extends AsyncTask<String, Void, Boolean> { 
    private final ProgressDialog dialog = new ProgressDialog(ManageData.this); 

    protected void onPreExecute() { 
      this.dialog.setMessage("Exporting database..."); 
      this.dialog.show(); 
    } 

    protected Boolean doInBackground(final String... args) { 
      [...]
    } 

    protected void onPostExecute(final Boolean success) { 
      if (this.dialog.isShowing()) { 
         this.dialog.dismiss(); 
      } 
    } 
} 

I am trying to refactor this so that the ExportDatabaseTask is declared in another class that is not the Activity, for various reasons, and I can't quite figure out how to make it work. I am lacking some basic Java concepts here, which I readily admit.

Specifically, myActivity is null in onPreExecute(). Why is that?

// this is a click event handler in my activity that starts the export
public void onClick(View v) {
  Exporter ex = new Exporter(getApplicationContext(), ActivityMain.this);
  ex.exportDatabaseTask.execute();
} 

//this is the utility class
public class Exporter {

    public ExportDatabaseTask exportDatabaseTask;   
    private Context myContext;
    private ActivityMain myActivity;

    public Exporter(Context ctx, ActivityMain act) {
            myContext = ctx;
            myActivity = act;

            this.exportDatabaseTask = new ExportDatabaseTask(); 
    }

    public class ExportDatabaseTask extends AsyncTask<Void, Void, Boolean> {
            private final ProgressDialog dialog = new ProgressDialog(myContext);

       // can use UI thread here?
       protected void onPreExecute() {
         // ====> this throws a Nullpointer exception:
         myActivity.dialog.setMessage("Exporting database...");
         myActivity.dialog.show();
       }

       protected Boolean doInBackground(final Void... args) {
      }

       protected void onPostExecute(final Boolean success) {
         if (myActivity.dialog.isShowing()) {
           myActivity.dialog.dismiss();
         }
       }
     }
}

I realize that I can take the code from the pre and post-execute events and put it into the onclick handler in my activity, avoiding this question, but I am still curious why the code above does not work.

+2  A: 

Rule #1: Never use getApplicationContext() to get a Context. Activity is a Context. You do not need another Context, and the Context you get from getApplicationContext() is utterly unusable for GUI-related operations.

Now, with that out of the way...

apparently it is possible to reference the activity's UI context from the onPreExecute and onPostExecute events

ExportDatabaseTask is a private inner class of the Activity. Inner classes in Java have access to the methods and data members of their outer class.

Specifically, myActivity is null in onPreExecute(). Why is that?

I doubt it is. I suspect that dialog is null inside of myActivity. That too will give you a NullPointerException on the indicated line.

CommonsWare
Thanks for the explanation. I decided that it is not a good idea for such a utility class to contain UI code and I moved the dialogs to the caller.
cdonner