3

I'm trying to return value from my asynctask in DoInBackground, but calling get() method freezes my UI. How can I re-write my code to a callback method? :

public class GetUrlDataTask extends AsyncTask<String, Integer, String> {
String response;
HttpUtils util;
@Override
protected String doInBackground(String... params) {
    try {
        util = new HttpUtils(params[0]);
        response = util.getContent();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return response;
}

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

In my activity I get result as response = new GetUrlDataTask().execute("site").get;

error_null_pointer
  • 457
  • 1
  • 6
  • 21
Sunstrike
  • 456
  • 1
  • 6
  • 22
  • That's the exact opposite of async. You need to use a callback. – SLaks Oct 22 '13 at 14:08
  • You can use intent with broadcastreceiver or maybe event bus, callback is a good idea too – An-droid Oct 22 '13 at 14:10
  • 1
    You can either make it an inner class and call `Activity` methods directly from `onPostExecute()` or [see this answer](http://stackoverflow.com/questions/18517400/inner-class-can-access-but-not-update-values-asynctask/18517648#18517648) about using an `interface` with `AsyncTask` – codeMagic Oct 22 '13 at 14:11

3 Answers3

6

You shouldn't use .get() if the Async task is going to take any decent amount of time (which it usually is).

Instead, you can either use a message/handler/service/etc, or you can simply use the onPostExecute(Result) method.

EDIT: New Code. Based on your description, it seems like you need to use an interface.

If you need to have Asynctask in another class, then an interface is probably your best option.

TestTask.java (your separate Asynctask):

import android.os.AsyncTask;

// Remember to change object type <> to what you need
public class TestTask extends AsyncTask<Object,Object,Object> {

    public interface OnTaskCompleted{
        void onTaskCompleted();
    }

    private OnTaskCompleted listener;

    public TestTask(OnTaskCompleted listener){
        this.listener = listener;
    }

    protected void onPostExecute(Object o){
        // Call the interface method
        if (listener != null)
            listener.onTaskCompleted();
    }

    @Override
    protected Object doInBackground(Object... params) {
        // The sleep() is just to simulate activity and delay
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

MainActivity.java (or any other activity):

public class MainActivity extends Activity {

private boolean status = false;

private OnTaskCompleted listener = new OnTaskCompleted() {
    public void onTaskCompleted() {
        status = true;
        Toast.makeText(MainActivity.this, "Status: " + status, Toast.LENGTH_SHORT).show();
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toast.makeText(MainActivity.this, "Status: " + status, Toast.LENGTH_SHORT).show();
    new TestTask(listener).execute("Testing");
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

}
Matt Joseph
  • 336
  • 1
  • 7
  • I have another class with asynctask with the same problem, but its very large, and i can include it in my activity class. – Sunstrike Oct 22 '13 at 14:36
  • Updated the original to reflect what you need. The code above is tested and works. You can change the interface to do what you need. The idea of the interface is more or less to have the method called by the Asynctask and executed inside of the activity, hence the callback concept. – Matt Joseph Oct 22 '13 at 15:00
  • But i still does not understand how to return value. I need to write something as "return String" in onTaskCompleted();? – Sunstrike Oct 22 '13 at 15:05
  • Waiting for a return value will freeze your UI. Instead, display a dialog or status indicator, and update the UI when the Asynctask is done. If you have another method that needs the return value, then you should restructure your code and instead continue once the callback is used. That way your UI is not waiting – Matt Joseph Oct 22 '13 at 16:49
1

I'm not a big fan of having AsycTask tasks in separate classes, especially if you need to use the response. It makes interacting with the response and local variables overly difficult considering how easy it is when implemented as an inner class.

I'm guessing you put it in its own class so you can reuse it. I would consider keeping the AsycTask as an inner class and calling outside reusable objects/methods in doInBackground(). This will keep the code DRY and allow your activity to do what it needs with the response.

public class MyActivity extends Activity {
    TextView textview;

    //...

    private class GetUrlTask extends AsyncTask<String, Integer, String> {

        protected String doInBackground(String... params) {
            return new GetHttpResponse().get(params[0]);
        }

        protected void onPostExecute(String response) {
            //Do UI updates...
            textview.setText(response);
        }
    }
}


public class GetHttpResponse {

    public String get(String url) {
        try {
            util = new HttpUtils(url);
            response = util.getContent();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    return response;
    }
}
Rumpel
  • 56
  • 3
0

You could do something like this:

public class MyActivity extends Activity
{
  public void someMethod()
  {
    // Here you could put up a ProgressDialog

    GetUrlDataTask myTask = new GetUrlDataTask();
    myTask.execute();
  }

  public class GetUrlDataTask extends AsyncTask<String, Integer, String>
  {
    @Override
    protected String doInBackground(String... params)
    {
      String response = null;
      HttpUtils util;

      try
      {
        util = new HttpUtils(params[0]);
        response = util.getContent();
      }
      catch (Exception e)
      {
        e.printStackTrace();
        response = e.getMessage();
      }
      return response;
    }

    @Override
    protected void onPostExecute(String result)
    {
      // Here you can dismiss the ProgressDialog and display the result
    }
  }
}
Jim Rhodes
  • 5,021
  • 4
  • 25
  • 38