Common class for AsyncTask in Android?
Asked Answered
C

3

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

Charters answered 20/7, 2010 at 15:16 Comment(0)
S
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.

Sarena answered 20/7, 2010 at 15:37 Comment(12)
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 ideaCharters
You have a onProgressMethod method that should do the work well, if you call publishProgress from your task for each url.Sarena
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!Formaldehyde
This really help me. thanks for asking the question and great answer.Calenture
what is the context in constructor and where is it used?Reducer
@mesuutt read the original question, the OP put that in his class constructor, I just posted modifications of the original code.Sarena
@Sarena 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 helpTide
@Tide 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.Sarena
@Sarena 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 lengthyTide
@Sarena i like your idea can you post a full example of this code.Discoid
@Discoid 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.Sarena
@Sarena 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 yoursDiscoid
B
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();



}
Bobette answered 11/3, 2012 at 19:36 Comment(0)
B
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.

Bedding answered 20/7, 2010 at 15:25 Comment(5)
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.Charters
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.Bedding
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.Formaldehyde
You need to popup the problem again in a new question.Bedding
@Bedding 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...Bubb

© 2022 - 2024 — McMap. All rights reserved.