3

I have an app that makes numerous RESTful service calls. I execute the calls in a class extending Asynctask. If I have to cancel the asynctask, I also want to cancel the service call. Unfortunately, cancelling the async operation still allows doInBackground to complete and I can't call isCancelled() once the request is waiting for a response (which can take a little bit). Right now, from within my doInBackground method I'm registering to be notified from the UI thread if a cancel request is made, so I can abort the HttpResponse object. Here is a piece of sample code.

It has worked so far, but can I really count on it, or am I just getting lucky? Can you count on one thread to call a method in another thread?

public class AsyncTestActivity extends Activity {

private ArrayList<IStopRequestMonitor> monitors;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main2);
}

public void stopActivity() {
    if (monitors == null || monitors.size() < 1) return;

    for (int i = 0; i < monitors.size(); i++) {
        monitors.get(i).stopRequest();
    }
}

public void addListener(IStopRequestMonitor listener) {
    if (monitors == null) monitors = new ArrayList<IStopRequestMonitor>();
    monitors.add(listener);
}

public void readWebpage(View view) {
    DownloadWebPageTask task = new DownloadWebPageTask();
    task.execute(new String[] { "http://www.mywebsite.com/feeds/rsstest.xml" });
}

private class DownloadWebPageTask extends AsyncTask<String, Void, String> {
    @Override
    protected String doInBackground(String... urls) {
        DefaultHttpClient client = new DefaultHttpClient();
        final HttpGet httpGet = new HttpGet(urls[0]);

        addListener(new IStopRequestMonitor() {

            public void stopRequest() {
                if (httpGet == null) return;
                httpGet.abort();
                cancel(true);
            }
        });

        try {
            HttpResponse execute = client.execute(httpGet);
            InputStream content = execute.getEntity().getContent();

            // handle inputstream
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    @Override
    protected void onPostExecute(String result) {
        Log.d("Result:", result);
    }
}

interface IStopRequestMonitor {
    public void stopRequest();
}
}
eimmer
  • 319
  • 1
  • 3
  • 13

2 Answers2

2

There is still a race here. If stopActivity() runs before the background thread has called addListener(), the listener will get added later and will never be called to abort the HttpGet.

If you are going to call cancel() from the UI thread (or whatever thread you create the AsyncTask on), you can:

  1. Create a 'private HttpGet httpGet' field in your AsyncTask.
  2. Override onPreExecute() and initialize httpGet there.
  3. Override onCancel() and say 'if (httpGet != null) { httpGet.abort() }'
  4. In doInBackground(), return immediately if isCancelled(), otherwise run.

Because this initializes httpGet on the UI thread, a cancel() call will either run before execute() (and therefore doInBackground will see isCancelled() return true), or it will run after httpGet exists and therefore the HttpGet will be aborted.

You don't need the listeners unless you are using that for something else.

rightparen
  • 1,693
  • 10
  • 15
1

you can define global object of asynctask class and using obj.cancle() method call on button click or whenever you need.

MR. Kumar
  • 661
  • 8
  • 25