40

I have a common class say for eg Class A which extends AsyncTask and has all the methods implemented i.e. onPreExecute, doinbackground and onPostExecute.

Now, there are other classes which want to use Class A object.

Say Class B uses class A in the below manner

A a = new A(context)
a.execute(url)

Then i fetch the result in get method. But get method is not the proper way of using AsyncTask. I will like to get the result in onPostExecute. For that i tried using a boolean parameter which will get true only in onpostexecute. The class B will check till it gets true and when it gets true it will fetch the result.

But this is somehow blocking the application.

I have placed the code for asynctask below.

'

import java.io.IOException;

import org.apache.http.client.ClientProtocolException;

import org.apache.http.client.HttpClient;

import org.apache.http.client.ResponseHandler;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.BasicResponseHandler;

import org.apache.http.impl.client.DefaultHttpClient;


import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;

public class A extends AsyncTask<String, Void, String> 
{
private Context context = null;

private final HttpClient httpClient = new DefaultHttpClient();

private String content = null;
//private String error = null;
private String finalResult = null;
private static boolean isResult = false;

private ProgressDialog progressDialog = null; 

public BabbleVilleSyncTask(Context context)
{
    this.context = context; 
    progressDialog = new ProgressDialog(this.context);
}

protected void onPreExecute() 
{
    progressDialog.setMessage("Please Wait....");
    progressDialog.show();
}

protected String doInBackground(String... urls) 
{
    try 
    {
        //urls[0] = URLEncoder.encode(urls[0], "UTF-8");

        HttpGet httpget = new HttpGet(urls[0]);
        ResponseHandler<String> responseHandler = new BasicResponseHandler();
        content = httpClient.execute(httpget, responseHandler);
    }
    /*catch(UnsupportedEncodingException ue)
    {
        error = ue.getMessage();
    }*/
    catch (ClientProtocolException e) 
    {
        //error = e.getMessage();
        cancel(true);
    }
    catch (IOException e) 
    {
        //error = e.getMessage();
        cancel(true);
    }

    httpClient.getConnectionManager().shutdown();

    return content;
}

protected void onPostExecute(String result) 
{
    finalResult = result;
    progressDialog.dismiss();
    System.out.println("on Post execute called");
    isResult = true;
}  

public boolean getIsResult()
{
    return isResult;
}

public void setIsResult(boolean flag)
{
    isResult = flag;
}

public String getResult()
{
    return finalResult;
}
}

'

Can someone let me know what the issue may be?

Regards

Sunil

willcodejavaforfood
  • 43,223
  • 17
  • 81
  • 111
sunil
  • 9,541
  • 18
  • 66
  • 88

3 Answers3

138

A clean way to use AsyncTask to get a result would be to use a callback interface.

Here is a simple example of this concept:

interface AsyncTaskCompleteListener<T> {
   public void onTaskComplete(T result);
}

then in your B class :

class B implements AsyncTaskCompleteListener<String> {

    public void onTaskComplete(String result) {
        // do whatever you need
    }

    public void launchTask(String url) {
        A a = new A(context, this);
        a.execute(url);
    }
}

you should now add the following code to your A class:

class A extends AsyncTask<String, Void, String> {
    private AsyncTaskCompleteListener<String> callback;

    public A(Context context, AsyncTaskCompleteListener<String> cb) {
        this.context = context;
        this.callback = cb;
    }

    protected void onPostExecute(String result) {
       finalResult = result;
       progressDialog.dismiss();
       System.out.println("on Post execute called");
       callback.onTaskComplete(result);
   }  
}

This way, you don't need to wait explicitely for your task to complete, instead, your main code (which is probably the main UI thread), is waiting in the normal android event loop, and the onTaskComplete method will be automatically called, allowing to handle the task result there.

SirDarius
  • 41,440
  • 8
  • 86
  • 100
  • Thanx a lot for this. But what if I want to call 3 urls in one execute method. Then how can we fetch the response? Any idea – sunil Jul 21 '10 at 06:48
  • 1
    You have a onProgressMethod method that should do the work well, if you call publishProgress from your task for each url. – SirDarius Jul 21 '10 at 08:20
  • could you elaborate some more? i'm currently trying to convert this method: public static int invokeWebServiceRequest(String uri, String username, String password, String targetKey); inside of a class that extends AsyncTask. how would i pass those paramaters (from my old method) to doInBackground(Params...)? thanks! – Cole Mar 10 '11 at 22:38
  • This really help me. thanks for asking the question and great answer. – Mike Aug 20 '12 at 11:51
  • what is the context in constructor and where is it used? – Mesut Tasci Oct 19 '12 at 13:41
  • @mesuutt read the original question, the OP put that in his class constructor, I just posted modifications of the original code. – SirDarius Oct 21 '12 at 16:20
  • 1
    @SirDarius That was a big help thanks. But is this generally a good coding practice in Android, or is it better to keep async tasks coupled with the Activity? I'm starting to move to your proposed design but wanted to ask what your thoughts are. I'm trying to use a more `Activity -> Helper -> DTO -> DAO -> DB/Web service` model and moving the Async task away from the activity certainly works with that model more. Thanks for any help – wired00 Nov 13 '12 at 23:51
  • 2
    @wired00 this approach does not enforce any kind of coupling, however be careful to note that the AsyncTask.postExecute method will be executed in the main UI thread, therefore the callback MUST NOT perform lengthy operations that might degrade user experience. – SirDarius Nov 14 '12 at 08:42
  • @SirDarius thanks i understand, that makes sense since postExecute has access to the activity's UI etc. That is fine, my callback is only ever alerting the Activity that its complete. it is not lengthy – wired00 Nov 14 '12 at 22:58
  • @SirDarius i like your idea can you post a full example of this code. – HeartlessArchangel May 29 '13 at 15:25
  • @HeartlessArchangel like a whole android app ? That would be a lot of code ! and what don't you understand in the code I posted ? It's pretty straightforward. – SirDarius May 29 '13 at 15:47
  • @SirDarius not a whole app just a sample implementation which is more clearer like complete class codes. i cant seem to get work. i really like this idea of yours – HeartlessArchangel May 30 '13 at 08:45
7
public abstract class BaseTask<T> extends AsyncTask<Object, Void, T> {

    public Context context;
    public ProgressDialog dialog;
    public Exception exception;

    protected BaseTask() {
    }

    public BaseTask(Context context) {
        this.context = context;
        this.dialog = new ProgressDialog(context);
    }

    @Override
    protected void onPreExecute() {
        this.dialog.setMessage(context.getResources().getString(R.string.loading));
        this.dialog.show();
    }

    @Override
    protected T doInBackground(Object... objects) {
        try {
           return doWork(objects);
        } catch (Exception e) {
            exception = e;
        }
        return null;
    }

    @Override
    protected void onPostExecute(T result) {
        if (dialog.isShowing()) dialog.dismiss();
        if (exception == null) {
            onResult(result);
        } else {
           onError();
        }
    }



    public abstract T doWork(Object... objects) throws Exception;
    public abstract void onResult(T result);
    public abstract void onError();



}
Georgy Gobozov
  • 13,633
  • 8
  • 72
  • 78
1

I would make class A a private class embedded in parent class, and once it's done with the work it should update the parent class properties, that's possible onPostExecute.

Pentium10
  • 204,586
  • 122
  • 423
  • 502
  • Yes, this is possible. But I have 15-20 activities and each having http connection. So, this is the reason I created a separate class for it. But now m facing the problem. So, is there any way to get the result from onPostExecute being in other class. – sunil Jul 20 '10 at 15:31
  • If you have 15+ activities all using http connection you need to subclass them somehow so same functionality to be defined in only 1 super class that you inherit from. – Pentium10 Jul 20 '10 at 18:20
  • please continue - as i'm trying to create a class/singleton for all of my http requests - that don't interrupt the main/UI thread. – Cole Mar 10 '11 at 22:33
  • You need to popup the problem again in a new question. – Pentium10 Mar 11 '11 at 06:58
  • @Pentium10 this is what I'm currently doing and I'll surely drop this method in my next project. Fortunately mine is small. Just to comment... – usr-local-ΕΨΗΕΛΩΝ Mar 25 '12 at 19:04