156

I have the following asynctask class which is not inside the activity. In the activity I'm initializing the asynctask, and I want the asynctask to report callbacks back to my activity. Is it possible? Or does the asynctask must be in the same class file as the activity?

protected void onProgressUpdate(Integer... values) 
{
    super.onProgressUpdate(values);
    caller.sometextfield.setText("bla");
}

Something like this?

zyamys
  • 1,609
  • 1
  • 21
  • 23
Asaf Nevo
  • 11,338
  • 23
  • 79
  • 154
  • I don't know about this. Sometimes, this may help you[AsyncTask Example](https://androidride.com/asynctask-android-tutorial-example/) – Athira Reddy Jun 03 '19 at 15:57

4 Answers4

410

You can create an interface, pass it to AsyncTask (in constructor), and then call method in onPostExecute()

For example:

Your interface:

public interface OnTaskCompleted{
    void onTaskCompleted();
}

Your Activity:

public class YourActivity implements OnTaskCompleted{
    // your Activity
}

And your AsyncTask:

public class YourTask extends AsyncTask<Object,Object,Object>{ //change Object to required type
    private OnTaskCompleted listener;

    public YourTask(OnTaskCompleted listener){
        this.listener=listener;
    }

    // required methods

    protected void onPostExecute(Object o){
        // your stuff
        listener.onTaskCompleted();
    }
}

EDIT

Since this answer got quite popular, I want to add some things.

If you're a new to Android development, AsyncTask is a fast way to make things work without blocking UI thread. It does solves some problems indeed, there is nothing wrong with how the class works itself. However, it brings some implications, such as:

  • Possibility of memory leaks. If you keep reference to your Activity, it will stay in memory even after user left the screen (or rotated the device).
  • AsyncTask is not delivering result to Activity if Activity was already destroyed. You have to add extra code to manage all this stuff or do you operations twice.
  • Convoluted code which does everything in Activity

When you feel that you matured enough to move on with Android, take a look at this article which, I think, is a better way to go for developing your Android apps with asynchronous operations.

Dmitry Zaytsev
  • 23,650
  • 14
  • 92
  • 146
  • should asyntask class be written in the manifest file ? – Asaf Nevo Apr 01 '12 at 11:44
  • @Dmitry, what other state of listener can we have other than onTaskCompleted? What about onPreExecute? – stuckedunderflow Mar 24 '13 at 00:14
  • 3
    @DmitryZaitsev very nice suggestion, I Think this is the best solution yet on non-private-inner class AsyncTask problems :-) – WiZarD May 08 '13 at 13:28
  • 2
    @DmitryZaitsev nice answer. but please tell me how I call the Asyntask now in my activity according to your code? – Qadir Hussain Sep 17 '13 at 12:30
  • 1
    Im trying this new HttpCallsAsyncTask(context, null, null, //Object OF the Interface).execute(); how can I pass the Interface Object in this case? and it is recommended that i must pass the object of interface because you have declared the default Constructor having the object of interface. please help – Qadir Hussain Sep 17 '13 at 12:34
  • @AsafNevo please help ? see my upper comments – Qadir Hussain Sep 17 '13 at 12:41
  • i don't really under stand your question... if you have a constructor that knows to get the interface as parameter, just save it in the asynctask and use it onPostExecute – Asaf Nevo Sep 17 '13 at 12:46
  • this corresponds to the delegate pattern, famously know on iOS; in your example, _listener_ is the delegate object that will be invoked by the task. – nburk May 06 '14 at 10:09
  • please mention how to pass interface from activity thanks – Ashish Sahu Oct 30 '14 at 00:30
  • @AshishSahu the same way as you normally call constructor with a parameter. – Dmitry Zaytsev Oct 30 '14 at 09:38
  • But it will not prevent memory leak as you are passing activity's instance to it – user1530779 Jul 06 '15 at 12:40
  • @user1530779 that is true. Not only it creates memory leaks, but it also hard to adjust to lifecycle of Activity. Consider using something else. – Dmitry Zaytsev Jul 07 '15 at 08:02
  • 2
    Shouldn't the asynctask use a weak-reference here? Since the task can leak the activity otherwise. – JacksOnF1re Nov 06 '15 at 12:43
  • 1
    @JacksOnF1re for the one hand - it should, of course. For the other hand, I think this would significantly complicate the answer, because I assume that it's primarily read by Android newcomers. Lastly, I don't think `AsyncTask` is a good thing to go with. There are quite some problems with using it, so I don't think it deserves any "improvements". Even if you would add `WeakReferece` you would still run into other problems with lifecycle. – Dmitry Zaytsev Nov 06 '15 at 13:00
  • nice solution, but for the clarity i prefer to name the interface AsyncTaskDelegate not OnTaskCompleted – luky Oct 31 '16 at 15:58
  • @luky consider __not__ using `AsyncTask` for reasons mentioned in my answer. – Dmitry Zaytsev Oct 31 '16 at 17:35
  • @Dmitry Zaitsev, "AsyncTask is not delivering result to Activity if Activity was already destroyed. You have to add extra code to manage all this stuff or do you operations twice. Could you say more about it? I mean, say more about possible scenarios and what do you mean by: "add extra code to manage all this stuff"? –  Jan 24 '17 at 17:15
  • using consumer would be more elegant – Hesham Yassin Jun 12 '18 at 20:30
  • what's the best way to detect which request created the response? in some cases, in the same Activity we'd have multiple different calls from different endpoints – Jurgen Cuschieri Sep 07 '18 at 14:15
  • @JurgenCuschieri you can pass an identifier in the constructor. However, instead of distinguishing between the requests it might make more sense to cancel currently running request before starting a new one. – Dmitry Zaytsev Sep 09 '18 at 20:05
49

I felt the below approach is very easy.

I have declared an interface for callback

public interface AsyncResponse {
    void processFinish(Object output);
}

Then created asynchronous Task for responding all type of parallel requests

 public class MyAsyncTask extends AsyncTask<Object, Object, Object> {

    public AsyncResponse delegate = null;//Call back interface

    public MyAsyncTask(AsyncResponse asyncResponse) {
        delegate = asyncResponse;//Assigning call back interfacethrough constructor
    }

    @Override
    protected Object doInBackground(Object... params) {

    //My Background tasks are written here

      return {resutl Object}

    }

    @Override
    protected void onPostExecute(Object result) {
        delegate.processFinish(result);
    }

}

Then Called the asynchronous task when clicking a button in activity Class.

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {

        Button mbtnPress = (Button) findViewById(R.id.btnPress);

        mbtnPress.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {

                MyAsyncTask asyncTask =new MyAsyncTask(new AsyncResponse() {

                    @Override
                    public void processFinish(Object output) {
                        Log.d("Response From Asynchronous task:", (String) output);          
                        mbtnPress.setText((String) output);
                    }
                });
                asyncTask.execute(new Object[] { "Youe request to aynchronous task class is giving here.." });

            }
        });
    }
}

Thanks

ThomasThiebaud
  • 11,331
  • 6
  • 54
  • 77
Arshad
  • 945
  • 9
  • 11
  • Again a memory leak situation – user1530779 Jul 07 '15 at 08:07
  • I was looking for the exact solution and found this thread, but I don't understand the reason behind doing all the above. In this case, the asynctask is executed in the MainActivity where access to the UI is not a problem. The same could have been accomplished by updating the UI in the onPostExecute() method of asynctask, without using a special interface for this. The problem that would be nice to solve is firing the async task from another non-activity class and update the UI from there. – user_noname_00 Jul 17 '15 at 05:09
  • same as you.. this is the best answer for begginers like me] – Aesthetic Nov 13 '16 at 11:37
  • @user1530779 why is this a memory leak situation? – user3764893 Jan 22 '18 at 22:13
  • @user3764893 If an Activity implements the Interface and is destroyed (i.e. due to and orientation change) before the AsyncTask is done, the AsyncTask holds a strong reference to the activity. Which causes the garbage collector to not collect the activity object. My suggested solution to this is pretty simple: set the delegate reference to null in the onDestroy() method of the activity. – Chris Feb 25 '18 at 13:34
6

IN completion to above answers, you can also customize your fallbacks for each async call you do, so that each call to the generic ASYNC method will populate different data, depending on the onTaskDone stuff you put there.

  Main.FragmentCallback FC= new  Main.FragmentCallback(){
            @Override
            public void onTaskDone(String results) {

                localText.setText(results); //example TextView
            }
        };

new API_CALL(this.getApplicationContext(), "GET",FC).execute("&Books=" + Main.Books + "&args=" + profile_id);

Remind: I used interface on the main activity thats where "Main" comes, like this:

public interface FragmentCallback {
    public void onTaskDone(String results);


}

My API post execute looks like this:

  @Override
    protected void onPostExecute(String results) {

        Log.i("TASK Result", results);
        mFragmentCallback.onTaskDone(results);

    }

The API constructor looks like this:

 class  API_CALL extends AsyncTask<String,Void,String>  {

    private Main.FragmentCallback mFragmentCallback;
    private Context act;
    private String method;


    public API_CALL(Context ctx, String api_method,Main.FragmentCallback fragmentCallback) {
        act=ctx;
        method=api_method;
        mFragmentCallback = fragmentCallback;


    }
Miguel
  • 3,349
  • 2
  • 32
  • 28
  • 3
    Idea is good, but be careful with anonymous implementations of `Main.FragmentCallback` - it might leak `Activity`. Consider using `WeakReference` to it. – Dmitry Zaytsev Jun 20 '14 at 07:47
  • 1
    Exactly why this question was asked bot of above answers leak activity if activity is destroyed while Asynctask is running as its anonymous inner class which has a reference to your activity an implicit reference – user1530779 Jul 07 '15 at 08:07
2

I will repeat what the others said, but will just try to make it simpler...

First, just create the Interface class

public interface PostTaskListener<K> {
    // K is the type of the result object of the async task 
    void onPostTask(K result);
}

Second, create the AsyncTask (which can be an inner static class of your activity or fragment) that uses the Interface, by including a concrete class. In the example, the PostTaskListener is parameterized with String, which means it expects a String class as a result of the async task.

public static class LoadData extends AsyncTask<Void, Void, String> {

    private PostTaskListener<String> postTaskListener;

    protected LoadData(PostTaskListener<String> postTaskListener){
        this.postTaskListener = postTaskListener;
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);

        if (result != null && postTaskListener != null)
            postTaskListener.onPostTask(result);
    }
}

Finally, the part where your combine your logic. In your activity / fragment, create the PostTaskListener and pass it to the async task. Here is an example:

...
PostTaskListener<String> postTaskListener = new PostTaskListener<String>() {
    @Override
    public void onPostTask(String result) {
        //Your post execution task code
    }
}

// Create the async task and pass it the post task listener.
new LoadData(postTaskListener);

Done!

ichalos
  • 497
  • 5
  • 9