0

I want to cancel an AsyncTask after a timeout if the task is still running and I want to cancel it immediately. Here is my code:

HttpURLConnection connection = null;
//GetRequestTask extends AsyncTask
final GetRequestsTask requestTask = new GetRequestsTask();
requestTask.execute(createUrl());
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
        Log.d("myapp", "entered handler");
        if (requestTask.getStatus() == AsyncTask.Status.RUNNING) {
            Log.d("myapp", "entered cancelling");
            requestTask.cancel(true);
            connection.disconnect();
        }
    }
}, TIME_OUT);

As you can see, I am calling a HTTP request in my AsyncTask's doInBackground method. and when I want to cancel the AsyncTask I also disconnect the connection.

The problem is that when I call cancel(true) and my app logs entered cancelling, the AsynkTask won't cancel immediately and will cancel with a minimum 10 to 20 seconds delay.

what should I do to cancel the task as soon as I call cancel?

UPDATE: This is my `AsyncTask code:

private class GetRequestsTask extends AsyncTask<URL, Void, JSONObject> {

        @Override
        protected void onPreExecute() {
            Log.d("myapp", "entered onPreExecute");
            mLoadingDialog.show();
        }

        @Override
        protected JSONObject doInBackground(URL... urls) {
            Log.d("myapp", "entered doInBackground");
            try {
                connection = (HttpURLConnection) urls[0].openConnection();
                connection.setRequestProperty("Accept", "application/json");
                int response = connection.getResponseCode();

                if (response == HttpURLConnection.HTTP_OK) {
                    StringBuilder builder = new StringBuilder();

                    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                    try {
                        String line;

                        while ((line = reader.readLine()) != null) {
                            builder.append(line);
                        }
                    }
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                    finally {
                        reader.close();
                    }

                    return new JSONObject(builder.toString());
                }
                else {

                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
            finally {
                connection.disconnect();
            }
            return null;
        }

        @Override
        protected void onPostExecute(JSONObject jsonObject) {
            Log.d("myapp", "entered onPostExecute");
            mLoadingDialog.dismiss();
            if (jsonObject == null) {
                showNoInternetDialog();
            }
            else {
                convertJSONtoArrayList(jsonObject);
                mRequestArrayAdapter.notifyDataSetChanged();
                mListView.smoothScrollToPosition(0);
            }
        }

        @Override
        protected void onCancelled() {
            Log.d("myapp", "entered onCancelled");
            mLoadingDialog.dismiss();
            showNoInternetDialog();
        }

The problem is that my timeout is 20 seconds, but onCancelled is called after 35, 40 seconds.

roostaamir
  • 1,928
  • 5
  • 24
  • 51

1 Answers1

0

From this question:

If you're doing computations:

  • You have to check isCancelled() periodically.

If you're doing a HTTP request:

  • Save the instance of your HttpGet or HttpPost somewhere (eg. a public field).
  • After calling cancel, call request.abort(). This will cause IOException be thrown inside your doInBackground.

Additionally if you use some library to provide convenient requests, then I think it should have some cancel() method. For example, for retrofit there is such method.

So in this case like for 'If you're doing a HTTP request' section. you need to:

  • Save the instance of your request (for retrofit it's Call)
  • After calling cancel, call 'cancel' method for saved instance of request.

And one more alternative is rxJava. With help of this library you can create Observable for your request and save reference to its subscription. Then you just need to call savedSubscription.unsubscribe() and that's it.

Also, please, do not use Async tasks as inner classes.

Graham
  • 7,431
  • 18
  • 59
  • 84
Michael Spitsin
  • 2,539
  • 2
  • 19
  • 29
  • there is no abort() for HTTPURLConnection class that I am using – roostaamir Jul 22 '16 at 08:19
  • Point is, if you call cancel you just telling to your async task that you WANT to cancel it. It will not stops immediatelly. In your case in while loop, when you parse string to builder check in every iteration that if asynctask is canceled – Michael Spitsin Jul 22 '16 at 08:44
  • The while loop only executes when the httpstatus is ok and everything went fine with the connection so it is useless there I think. Can I change the `HttpURLConnection` to something that has an `abort()` function? and how? – roostaamir Jul 22 '16 at 08:54
  • If you will have big data passed through url connection, then you will need some time to convert stream to String. During this time you can cancel your task from outside. So it's not useless. – Michael Spitsin Jul 22 '16 at 08:55
  • As for other. I would suggest to not use AsyncTask. You can use RxJava for exmaple and just call `unsubscribe`. If you want to use exactly AsyncTask, then you can provide HttpGet, it has abort() method. In case of HttpUrlConnection there is `disconnect` method, but I think it will not immediate stop connection. You can try one of these ways – Michael Spitsin Jul 22 '16 at 08:58