54

I have the following class:

public class getURLData extends AsyncTask<String, Integer, String>{

@Override
protected String doInBackground(String... params) {
    String line;
    try {  
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(params[0]);

        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        line = EntityUtils.toString(httpEntity);

    } catch (UnsupportedEncodingException e) {
        line = "<results status=\"error\"><msg>Can't connect to server</msg></results>";
    } catch (MalformedURLException e) {
        line = "<results status=\"error\"><msg>Can't connect to server</msg></results>";
    } catch (IOException e) {
        line = "<results status=\"error\"><msg>Can't connect to server</msg></results>";
    }
    return line;
}

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

}

And I am trying to call it like this:

String output = null;
output = new getURLData().execute("http://www.domain.com/call.php?locationSearched=" + locationSearched);

But the output variable isn't getting data, instead I am getting an error:

Type mismatch: cannot convert from AsyncTask<String,Integer,String> to String
Jonathan Soifer
  • 2,715
  • 6
  • 27
  • 50
Paul
  • 2,465
  • 8
  • 35
  • 60

8 Answers8

113

The method execute returns the AynscTask itself, you need to call get:

output =
    new getURLData()
        .execute("http://www.example.com/call.php?locationSearched=" + locationSearched)
        .get();

This will start a new thread (via execute) while blocking the current thread (via get) until the work from the new thread has been finished and the result has been returned.

If you do this, you just turned your async task into a sync one.

However, the problem with using get is that because it blocks, it needs to be called on a worker thread. However, AsyncTask.execute() needs to be called on the main thread. So although this code could work, you may get some undesired results. I also suspect that get() is under-tested by Google, and it is possible that they introduced a bug somewhere along the line.

Reference: AsyncTask.get

Steve Strates
  • 433
  • 3
  • 13
K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • 5
    My god this gets more complicated the further I go in... All I want to do is when a user clicks a button it loads the screen straight way, then displays the content from the http when its finished. How is this done? – Paul Jun 10 '12 at 20:36
  • 3
    @Paul: Whatever you need to do after execution, do it on `onPostExecute`. Is _that_ simple... – K-ballo Jun 10 '12 at 20:37
  • 2
    I wish it was, but if I move move to the onPostExecute method I don't have the params variables any more and more issues arise returning the string. I appreciate your help, but this has taken me hours, I really think this is a lost cause. – Paul Jun 10 '12 at 20:46
  • 4
    @Paul: You can add a constructor to `getURLData` that takes the parameters and stores them as class members, then they are available at your `onPostExecute` method. – K-ballo Jun 10 '12 at 20:48
  • 1
    Thank you for your help and patience. I managed to get it working! Nice one :) – Paul Jun 11 '12 at 09:05
  • you better put the consequent action(s) after getting the result string at PostExecute – Jeff Bootsholz Sep 18 '13 at 03:37
  • There is no guarantee that such AsyncTask performs backend thread faster than UI Main Thread – Jeff Bootsholz Sep 18 '13 at 03:37
  • Also, it returns an AsyncTask variable which is not useful? – Kingsley Mitchell Oct 18 '17 at 22:31
18

I'd rather create callback than block UI thread. In your class create method which will be invoked when data arrive. For example:

private void setData(String data){
    mTextView.setText(data);
}

Then in AsyncTask implement onPostExecute:

@Override
protected void onPostExecute(String result) {
    setData(result);
}

And then somewhere in code just execute task:

new getURLData().execute(...

When task finishes setData is invoked and mTextView is filled.

AsyncTask.get() will blok your UI, so there is no reason to use AsyncTask.

pawelzieba
  • 16,082
  • 3
  • 46
  • 72
  • 10
    great, but how can I call `setData` from the AsyncTask, if that method is in the other activity (the one that makes the `.execute`) ? – Francisco Corrales Morales May 09 '14 at 15:41
  • 2
    You don't. Just do what you need in [onPostExecute()](http://developer.android.com/reference/android/os/AsyncTask.html#onPostExecute%28Result%29) – Cyrille Feb 01 '16 at 13:10
  • @FranciscoCorralesMorales It's better to define interface to use it like this answer: https://stackoverflow.com/a/12575319/4616096 – Kaveh Safavi Mar 25 '18 at 10:07
4

If the user clicks the button, then has to wait for the content, what do they do meanwhile?

Why not do this:

  1. User clicks button.
  2. You check for connectivity. If the user isn't connected to the Internet, tell them.
  3. You start an IntentService by sending an Intent to send the HTTP request.
  4. When the request finishes, you post a notification.
  5. The user clicks the notification, which returns to an Activity that can do the next step.

This allows the user to go off and do whatever while the request is being processed.

An IntentService runs in the background on its own thread. When it receives an Intent, it runs onHandleIntent(). When that method finishes, the Service is cached: it's not active, but it can re-start quickly when the next Intent arrives.

Joe Malin
  • 8,621
  • 1
  • 23
  • 18
2
  1. Get the context in the constructor like this:

    public GetDataTask(Context context) {}

  2. Create an Interface with the method:

    void onDataRetrieved(String data);

  3. Implement the Interface in the class from where you are creating the Task object (e.g. MainActivity)

  4. Cast the Context to the Interface and call the onDataRetrieved Method

Community
  • 1
  • 1
Matthias Herrmann
  • 2,650
  • 5
  • 32
  • 66
  • This can cause a memory leak. If a long running operation has access to the activity's context, the asynctask would prevent the actvity from being garbage collected in the event of configuration change or user pressing back from the activity for some reason. – Rahul Sainani Aug 17 '17 at 09:23
  • 1
    It is necessary to use `WeakReference actWeak = new WeakReference<>(activity)` when you send an `Activity` to `AsyncTask` to avoid memory leaks and get it back with `Activity act = actWeak.get()` in the `doInBackground` method. – isabsent Dec 01 '17 at 04:47
0

In the following code, I get a String (directorName) back from AsyncTask.

public class GetDirector {
    String id;
    private String baseURL = "http://www.omdbapi.com/?i=";
    private String finalURL = "";
    String theDirector;

    public GetDirector(String imdbID) throws ExecutionException, InterruptedException {
        id= imdbID;
        finalURL = baseURL + id + "&plot=full&r=json";
        System.out.println("GetDirector. finalURL= " + finalURL);
        theDirector = new GetDirectorInfo().execute().get();
    }

    public String getDirector (){
        return theDirector;
    }

    private class GetDirectorInfo extends AsyncTask<Void, Void,String> {
        @Override
        protected String doInBackground(Void... params) {
            String directorName = null;

            ServiceHandler sh = new ServiceHandler();

            // Making a request to url and getting response
            String jsonStr = sh.makeServiceCall(finalURL, ServiceHandler.GET);
            System.out.println("Act_DetailsPage. jsonStr= " + jsonStr);

            if (jsonStr != null) {
                try {
                    JSONObject everything = new JSONObject(jsonStr);

                    directorName = everything.getString(JSON_Tags.TAG_DIRECTOR);
                    System.out.println("directorName= "+ directorName);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println("Inside GetDirector. Couldn't get any data from the url");
            }
            return directorName;
        }
    }
}
Gene
  • 10,819
  • 1
  • 66
  • 58
  • 4
    By using *get* you are going synchronous, thus defeating the whole purpose of AsyncTask which is to perform asynchronously. – mradzinski Dec 09 '15 at 13:38
0

Add a context parameter to task's constructor which would refer to object where you'd store resulting data.

class PopulateArray extends AsyncTask<Integer, Integer, ArrayList<String>>
{
    Context _context; // context may be what ever activity or object you want to store result in
    PopulateArray(Context context)
    {
        _context = context;
    }
    @Override
    protected ArrayList<String> doInBackground(Integer... integers)
    {
        ArrayList<String> data = new ArrayList<String>();
        //manipulate
        return data;
    }

    @Override
    protected void onPostExecute(ArrayList<String> strings)
    {
        _context.setData(strings);
    }
}
Dragas
  • 1,140
  • 13
  • 29
0

This ONE LINER worked for me:

String result = MyasyncTask.execute(type, usrPhoneA, usrPWDA).get();
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
0

The only way to send data from AsyncTask to UI without pain is Otto or Event bus. Register a method which will handle a result under @Subscribe annotation in UI and post a message with a result to it in onPostExecute method of your AsyncTask.

isabsent
  • 3,683
  • 3
  • 25
  • 46