How to handle return value from AsyncTask

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?


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.
}

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.