19

I am using AsyncTask class with the following signature:

public class ApiAccess extends AsyncTask<List<NameValuePair>, Integer, String> {
    ...
private String POST(List<NameValuePair>[] nameValuePairs){
    ...
    return response;
}
}

protected String doInBackground(List<NameValuePair>... nameValuePairs) {
    return POST(params);
}

I am trying to call it from other class through:

ApiAccess apiObj = new ApiAccess (0, "/User");
// String signupResponse = apiObj.execute(nameValuePairs);
String serverResponse = apiObj.execute(nameValuePairs); //ERROR

But here I get this error:

Type mismatch: cannot convert from AsyncTask<List<NameValuePair>,Integer,String> to String

Why is that when i have specified String as the third parameter in Class extension line?

IgniteCoders
  • 4,834
  • 3
  • 44
  • 62
Maven
  • 14,587
  • 42
  • 113
  • 174
  • The method execute return the `AsyncTask` not the result. If you want to return some result from the asyncTask or provide some data, use [protocols](http://stackoverflow.com/a/26820666/2835520) – IgniteCoders Nov 08 '14 at 19:03

4 Answers4

34

You can get the result by calling AsyhncTask's get() method on the returned AsyncTask, but it will turn it from an asynchronous task into a synchronous task as it waits to get the result.

String serverResponse = apiObj.execute(nameValuePairs).get();

Since you have your AsyncTask in a seperate class, you can create an interface class and declare it in the AsyncTask and implement your new interface class as delegate in the class you wish to access the results from. A good guide is here: How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?.

I will attempt to apply the above link to your context.

(IApiAccessResponse)

public interface IApiAccessResponse {
    void postResult(String asyncresult);
}

(ApiAccess)

public class ApiAccess extends AsyncTask<List<NameValuePair>, Integer, String> {
...
    public IApiAccessResponse delegate=null;
    protected String doInBackground(List<NameValuePair>... nameValuePairs) {
        //do all your background manipulation and return a String response
        return response
    }

    @Override
    protected void onPostExecute(String result) {
        if(delegate!=null)
        {
            delegate.postResult(result);
        }
        else
        {
            Log.e("ApiAccess", "You have not assigned IApiAccessResponse delegate");
        }
    } 
}

(Your main class, which implements IApiAccessResponse)

ApiAccess apiObj = new ApiAccess (0, "/User");
//Assign the AsyncTask's delegate to your class's context (this links your asynctask and this class together)
apiObj.delegate = this;
apiObj.execute(nameValuePairs); //ERROR

//this method has to be implement so that the results can be called to this class
void postResult(String asyncresult){
     //This method will get call as soon as your AsyncTask is complete. asyncresult will be your result.
}
Community
  • 1
  • 1
frogmanx
  • 2,620
  • 1
  • 18
  • 20
3

I would suggest implementing a Handler Callback. You would pass the fragment's (or activity's) Handler to the AsyncTask, which the AsyncTask will call when it is finished. The AsyncTask can also pass back an arbitrary object.

Here is an example AsyncTask, which I have in its own file (not subclassed):

public class MyTask extends AsyncTask<Void, String, String> {

    private static final String TAG = "MyTask";
    private Handler mCallersHandler;
    private Candy    mObject1;
    private Popsicle mObject2;

    // Return codes
    public static final int MSG_FINISHED = 1001;

    public SaveVideoTask(Handler handler, Candy candyCane, Popsicle grapePop ) {
        this.mCallersHandler = handler;
        this.mObject1        = candyCane;
        this.mObject2        = grapePop;
    }

    @Override
    protected String doInBackground(Void... params) {

        // Do all of the processing that you want to do...
        // You already have the private fields because of the constructor
        // so you can use mObject1 and mObject2
        Dessert objectToReturn = mObject1 + mObject2;

        // Tell the handler (usually from the calling thread) that we are finished, 
        // returning an object with the message
        mCallersHandler.sendMessage( Message.obtain( mCallersHandler, MSG_FINISHED, objectToReturn ) );

        return (null);
    }
}

This example assumes that your AsyncTask needs a piece of Candy and a Popsicle. Then it will return a Dessert to your fragment.

You can construct and run the AsyncTask in one line from your fragment with:

( new MyTask( mFragmentHandler, candyCane, grapePop ) ).execute();

But of course, you will first need to set up the fragment's handler (myFragmentHandler). To do this, your fragment (or activity) should look like (NOTE the "implements Handler.Callback"):

public class MyFragment extends Fragment implements Handler.Callback {

    private Handler mFragmentHandler;
    private Candy candyCane;
    private Popsicle grapePop;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        // Standard creation code
        super.onCreate(savedInstanceState);
        setRetainInstance(true);

        // Create a handler for this fragment 
        mFragmentHandler = new Handler(this);

        // Other stuff...
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup parent,
            Bundle savedInstanceState) {

        // Inflate the layout
        View v = inflater.inflate(R.layout.my_fragment_layout, parent, false );

        // The candyCane and grapePop don't need to be set up here, but 
        // they MUST be set up before the button is pressed. 
        // Here would be a good place to at least initialize them...

        // Perhaps you have a button in "my_fragment_layout" that triggers the AsyncTask...
        Button mButton  = (Button) v.findViewById(R.id.mButton);
        mButton.setOnClickListener( new OnClickListener() {
            @Override
            public void onClick(View v) {
                ( new MyTask( mFragmentHandler, candyCane, grapePop ) ).execute();
            }
        });
        return v;
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean handleMessage(Message msg) {

        switch (msg.what) {

        case MyTask.MSG_FINISHED:

            // Let's see what we are having for dessert 
            Dessert myDessert = (Dessert) msg.obj;
            break;

        }
        return false;
    }

}

If you use these pieces of code, a button press will trigger the AsyncTask. The calling fragment will continue to execute while the AsyncTask is processing. Then, when the AsyncTask is finished, it will send a message to the fragment saying that it is finished, and pass an object with the message. At this point, the fragment will see the message, and do whatever you want.

Note: There might be typos. This is cut from a very large and complicated code.

bremen_matt
  • 6,902
  • 7
  • 42
  • 90
  • i think we should keep a week reference of handler inside AsyncTask – Sayem Apr 26 '16 at 06:54
  • There is a reference. I am not sure what you mean. I think the code formatting was bad, so I tried to fix it. Perhaps it was not initially apparent. – bremen_matt Apr 28 '16 at 08:42
  • Thanks Bremen_matt for this solution. I was able to fix syncing problems between two Async tasks using this solution. – Sachiin Gupta Oct 19 '16 at 20:20
0

The problem is that when you call execute, the AsyncTask object is returned, but not the result yet. The result is computed in the background. The type of the result will eventually be a String (as you specified), and will be passed to onPostExecute().

You should use the AsyncTask as follows:

public class ApiAccess extends AsyncTask<List<NameValuePair>, Integer, String> {
    ...
    private String POST(List<NameValuePair>[] nameValuePairs){
    ...
        return response;
    }

    protected void onPreExecute (){
        // this is run on the main (UI) thread, before doInBackground starts
    }

    protected void onPostExecute (String result){
        // this is run on the main (UI) thread, after doInBackground returns
    }

    protected String doInBackground(List<NameValuePair>... nameValuePairs) {
        // run in another, background thread
        return POST(params);
    }
}

Note that in your example you are not returning the result in doInBackground(), which you should.

Jeffrey Klardie
  • 3,020
  • 1
  • 18
  • 23
-1

Please read AsyncTask. You can get result on onPostExecute method. You can't do something like :

String serverResponse = apiObj.execute(nameValuePairs); 

because it is async.

Devrim
  • 15,345
  • 4
  • 66
  • 74