I have the following code block:
try {
URL url = new URL("http://site-to-test.com/nonexistingpage.html");
HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
urlc.setRequestProperty("User-Agent", CoreProtocolPNames.USER_AGENT);
urlc.setRequestProperty("Connection", "close");
urlc.setConnectTimeout(500); // timeout is in milliseconds
urlc.connect();
if (urlc.getResponseCode() == 404) {
// Server was reachable
Log.i(TAG, "Server is reachable");
}
} catch (MalformedURLException mue) {
Log.e(TAG, "MalformedURLException: " + mue.getMessage());
} catch (IOException e) {
Log.e(TAG, "IOException: " + e.getMessage());
}
When the site-to-test
domain is not reachable through the current network, this code blocks for about 30-40 seconds before receiving the IOException
. And I specifically set the timeout value to 500ms. What am I missing here? Shouldn't the above block terminate in half a second, regardless of the network state and the site's availability?
It appears that Java URLConnection provides no fail-safe timeout on reads
The solution is, as the article explains, to use a separate Thread for timing, and disconnect the HttpURLConnection manually once the timer thread is done.
After deep investigations and alot of trails, I found that the best way to implement a timer for AsyncTask (or Service, the object you used to perform background work) away from the HTTP connection class, as sometimes when you disconnect the HTTP connection, this doesn't interrupt the web call, I implemented this class to be used when you need timeout check for your HTTP connection
public abstract class AsyncTaskWithTimer<Params, Progress, Result> extends
AsyncTask<Params, Progress, Result> {
private static final int HTTP_REQUEST_TIMEOUT = 30000;
@Override
protected Result doInBackground(Params... params) {
createTimeoutListener();
return doInBackgroundImpl(params);
}
private void createTimeoutListener() {
Thread timeout = new Thread() {
public void run() {
Looper.prepare();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (AsyncTaskWithTimer.this != null
&& AsyncTaskWithTimer.this.getStatus() != Status.FINISHED)
AsyncTaskWithTimer.this.cancel(true);
handler.removeCallbacks(this);
Looper.myLooper().quit();
}
}, HTTP_REQUEST_TIMEOUT);
Looper.loop();
}
};
timeout.start();
}
abstract protected Result doInBackgroundImpl(Params... params);
}
A Sample for this
public class AsyncTaskWithTimerSample extends AsyncTaskWithTimer<Void, Void, Void> {
@Override
protected void onCancelled(Void void) {
Log.d(TAG, "Async Task onCancelled With Result");
super.onCancelled(result);
}
@Override
protected void onCancelled() {
Log.d(TAG, "Async Task onCancelled");
super.onCancelled();
}
@Override
protected Void doInBackgroundImpl(Void... params) {
// Do background work
return null;
};
}