12

I use AsyncTask to connect an URLPath as below code.

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

    Button btn = (Button)findViewById(resid);
    btn.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            //how to stop connect
        }
    });
    new Connecting().execute();
}


class Connecting extends AsyncTask<String, String, String> {
    @Override
    protected void onPreExecute() {
            super.onPreExecute();
        //do something
    }

        @Override
        protected String doInBackground(String... aurl) {
            try {
                URL url = new URL(URLPath);
                connection = (HttpURLConnection)url.openConnection();
                connection.setConnectTimeout(30000);
                connection.setReadTimeout(30000);
                connection.setDoInput(true);
                connection.setUseCaches(false);
                connection.connect();
                is = connection.getInputStream();

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

        @Override
        protected void onPostExecute(String unused) {
            super.onPostExecute(unused);
        //access InputStream is
        }
}

connection may spend much time in connect.
While it connecting, I want to set the Bbutton btn is pressed to stop the connection connect.
How can I set in setOnClickListener method?

brian
  • 6,802
  • 29
  • 83
  • 124

2 Answers2

22

Wrap the code that uses HttpURLConnection inside a Future, which you can cancel at will. You can also use the Future's timeout feature, since the timeouts in HttpURLConnection are not reliable.

Define an executor and future within your networking class:

final ExecutorService executor = Executors.newCachedThreadPool(Executors.defaultThreadFactory());
Future<MyResult> future;

Then wrap your networking code inside the Future like this:

future = executor.submit(new Callable<MyResult>() {
    @Override
    public MyResult call() throws IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        try {
            // .. use the connection ...
        } finally {
            connection.disconnect();
        }
    }
});

MyResult result = future.get(TIMEOUT, TimeUnit.SECONDS);

When you want to cancel, you can make this call from any thread:

future.cancel(true);

If the future times out or is canceled, the connection task may continue to block on a HttpURLConnection method, but your application won't be held back. You should still set appropriate timeouts on the connection to speed up freeing of threads and network sockets.

Edward Brey
  • 40,302
  • 20
  • 199
  • 253
  • 7
    If the HttpURLConnection is waiting on a read, this does NOT work. I'm testing it right now and the threads never go away after the cancel. I've created a test where the target server has had an out of memory condition, it can accept connections but never returns. The above code is never able to cancel the thread. – D-Klotz Sep 11 '15 at 17:51
  • @D-Klotz The thread pool threads are supposed to stick around for later reuse. That's an implementation detail of `ExecutorService`. The important thing is that `Future.get` promptly times out and returns. – Edward Brey Sep 11 '15 at 17:57
  • @EdwardBrey Sorry I'm not stating things correctly. When I suspend these threads they are still within : SocketInputStream.socketRead0(FileDescriptor, byte[], int, int, int) line: not available [native method] and this is after the cancel(true); – D-Klotz Sep 11 '15 at 18:05
  • 1
    @D-Klotz `Future.cancel` [interrupts](http://stackoverflow.com/q/3590000/145173) the connection task. Among its many problems, `HttpURLConnection` seems to ignore the interrupt. Fortunately, as long as your app only considers whether the Future has completed (including timing out or being canceled), it can ignore the languishing task, knowing that it will eventually go away. – Edward Brey Sep 11 '15 at 18:17
  • @EdwardBrey I'm not sure how wise it is to ignore threads especially with heavy traffic. These threads will be around for at least two hours (default tcp timeout). Do we really want to allow the number of threads to grow unchecked? I've determined that the real solution is to set the readTimeout of the HttpURLConnection itself to a value appropriate for the connection. Thanks for your responses. – D-Klotz Sep 11 '15 at 18:43
  • @D-Klotz A future isn't a replacement for connection timeouts, but rather a supplement. I updated my answer to clarify. Unfortunately, the timeouts in `HttpURLConnection` don't cover all the cases. It can get in states where it blocks well beyond the longest timeout you've set. – Edward Brey Sep 11 '15 at 18:58
  • The threads wont go away because read/writes do not respond to interrupts. The only way they will stop is when the read/write times out. AFAIK you can't configure the write timeout. – Luke Mar 19 '20 at 01:03
-1

Save the HttpURLConnection response by (HttpURLConnection)url.openConnection(); as a class member: HttpURLConnection mHttpURLConnection = (HttpURLConnection)url.openConnection();

after you call future.cancel(true); then call mHttpURLConnection.disconnect()

so the network request will end as fast as possible