4

This is one I'm not sure how to go about. Basically I have an ASyncTask class thats doing its business as usual in the background. I'd like to do something after its finished. Now before you jump ahead and say "just use onPostExecute()", theres a catch. The method I need run is in the activity and not the Task class.

The way I see it, I have 2 options.

A:

    CustomTask task = new CustomTask();
    task.execute(passedParams);
            //when(task.execute is finished)
            {
                doX();
            }

I hope I can do it this way as Its so simple and lets me check when the task is completed without having to constantly poll it for activity and getStatus() on the activity. I don't think I'll get this lucky but If anyone has a way of doing it, that'd be great

B:

Pass the activity as a paramater to the ASyncTask. This is messy and I'm not happy about using it but asides from that and the object reference, I don't know if it will work

    CustomTask task = new CustomTask();
    task.execute(passedParams,MyActivity);

Then in the Tasks onPostExecute, I can just have it call the MyActivity.doX();


C:

A third way would be to make the asynctask a private class in the activity itself but i really would like to keep it separate. Resusability and what not –

Any thoughts on this?

To summarize, Need to doX() after task.execute is finished. Any ideas appreciated.


D:

Ok I know I'm on a roll here. I keep thinking up new solutions. A class method or static method that can be called from any where.

public class ProfileSettings extends Activity
{
      public static void doX()
      {
          //Logic...
      }
}

From AsyncTask

MyActivity.doX();
OVERTONE
  • 11,797
  • 20
  • 71
  • 87
  • A third way would be to make the asynctask a private class in the activity itself but i really would like to keep it separate. Resusability and what not – OVERTONE Apr 11 '12 at 23:11
  • @Cristian What a helpful comment. Yes I have. – OVERTONE Apr 11 '12 at 23:20
  • ok D is pretty stupid. I'm just throwing out ideas – OVERTONE Apr 11 '12 at 23:20
  • I you have, do it again. And again, until you finally understand. – Cristian Apr 13 '12 at 01:48
  • 1
    @Cristian OR.... I could ask for help instead of bashing my head against a wall which it seems is your "again and again style". In asking this question I discovered the concept of anonymous inner classes in Kabuko's answer. I've solved my problem and am more enlightened for it. So kindly get lost if those are the most productive comments you can make. – OVERTONE Apr 13 '12 at 14:39

4 Answers4

6

Option B should work and is sometimes a good option, but sometimes I use anonymous classes for this. When you call it from your activity:

CustomTask task = new CustomTask() {
    @Override
    protected void onPostExecute(Long result) {
        super.onPostExecute(result);
        MyActivity.this.doX();
    }
}.execute();
kabuko
  • 36,028
  • 10
  • 80
  • 93
  • Wow. I dunno if that'll work but thats pretty clever regardless :) – OVERTONE Apr 11 '12 at 23:22
  • That took some tinkering but I did not even consider anon classes. Its not something I would use in excess as then it would be confusing but for my problem, Its perfect. I get to use the Asyncs OnPostExecute (The way it was intended) and I don't have to pass the entire activity in. Thanks for that. Its quite the trick – OVERTONE Apr 13 '12 at 14:41
3

Option A:

Android API has already provided built-in function for this purpose AsyncTask.get():

CustomTask task = new CustomTask();
task.execute(passedParams);
Result result = task.get(); // <- Block UI thread and waiting for AsyncTask finish.
this.doX(result);

As you can see, this is a bad practice as it blocks UI thread and may cause ANR exception, By doing this, you are actually sacrifice the benefit of AsyncTask, and make it running synchronously with UI thread.


Option B and C:

Both are correct way of doing things, by calling doX() method in onPostExecute() method,

AsyncTask, as its name stated, run a background thread asynchronously with UI thread, and once the background thread is finished, onPostExecute method is called on UI thread. There is no way to tell exactly when onPostExecute method is called (i.e. when doInBackground method is finished) at project build time, as it is determined at app run time, the only thing we know is onPostExecute method is guaranteed to be called on UI thread at some point in the future, In another word, when writing code at project build time, we never know exactly when the doInBackground is finished and code execution jump back to UI thread outside onPostExecute method (Unless you implement some waiting mechanism in code like Option A). So the purpose of onPostExecute method is for processing everything after doInBackground method is finish, this is also why the only argument of onPostExecute method is the result returned from doInBackground method.

The difference between Option B and C is whether to implement AsyncTask as inner class or separate class. This has been aksed and discussed many times in StackOverflow. Most people think it is good to separate them for resusability reason or etc. From my point of view, I don't agree with it. Java Programming Language has its reason provide inner class syntax to suit some special coding situations, when talking about code refactoring from a OOP perspective, think more from problem abstraction level, not simply strip inner class out from Activity class at code level. As you can see in your example, by isolating AsyncTask from Activity, you don't gain any real benefit but rather increase the code complexity (need pass activity context reference between classes) to solve problem.

I think you real question is whether or not we should isolate AsyncTask inner class implementation from Activity. For a better OOP code refactoring (reusability, testability and etc.), checkout my answer in this StackOverflow question to see how to isolate business layer from application UI layer properly.

Community
  • 1
  • 1
yorkw
  • 40,926
  • 10
  • 117
  • 130
1

I was able to implement this feature with an interface:

http://howcanisolve.com/38646/android-java-equivalent-of-ios-block-callbacks

public interface ICallbacks { 
public void onResponse(JSONObject response);
public void onError(VolleyError error);
}

Then in your routine code just put a new instance of Callbacks:

public static void getPassagesForFirebaseUser(FirebaseUser user,
Context context, ICallbacks events) { 
  //here code and call ICallbacks methods
  if(result){ events.onResponse(response); }
  if(error){ events.onError(err); }
}

ultimately you can call the method with :

getPassagesForFirebaseUser(user, context, new ICallbacks(){
  @Override
  public void onResponse(JSONObject response){
      //Success !!!
  }
  @Override
  public void onError(VolleyError response){
      //Error !!!
  }
});
Pablo Alfonso
  • 2,317
  • 1
  • 18
  • 24
0

Option B is generally safer. But even then, you need to be careful. You need to store the instance of your Activity (not just the class) in the ASyncTask. And if the Activity gets destroyed while the task is running (what if the user presses the Back button?), you need to inform the task of this so the task doesn't try to call a method on a dead Activity.

And if the Activity comes back to life (after a screen rotation, for example) then you need to reattach the new Activity to the running task.

These things are fiddly.

Graham Borland
  • 60,055
  • 21
  • 138
  • 179
  • Good point. For my application, the orientation is fixed but you never know when that might change. Hmmm.... – OVERTONE Apr 11 '12 at 23:21
  • @OVERTONE That's just a reality you have to face w/Android apps. There are a lot of ways an activity can die and you just have to go through that pain and account for it when you deal with AsyncTasks unfortunately. [Loaders](http://developer.android.com/reference/android/content/Loader.html) including [AsyncTaskLoader](http://developer.android.com/reference/android/content/AsyncTaskLoader.html) and [LoaderManager](http://developer.android.com/reference/android/app/LoaderManager.html) make this a bit better. – kabuko Apr 11 '12 at 23:30