In an Android app, I'm trying to test that the user has a working Internet connection. If you are interested, there is some background in a previous question Detecting limited network connectivity in Android?
The code is basically like:
try {
HttpParams myParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(myParams, 10000);
HttpConnectionParams.setSoTimeout(myParams, 10000);
httpClient = new DefaultHttpClient(myParams);
request = new HttpHead(url);
response = httpClient.execute(request);
statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200)
{
return false;
}
return true;
} catch(Exception e) {
return false;
}
I can control the timeouts for Connection and Socket using HttpConnectionParams. But, if my device is connected to Wifi, but the wifi has no Internet access, the error I'm getting in the exception is:
libcore.io.GaiException: getaddrinfo failed: EAI_NODATA (No address
associated with hostname)
Unable to resolve host "www.example.com": No address associated with
hostname
Which looks like it timed out on a DNS lookup. Can I control the timeout of the DNS Lookup? httpClient.execute is taking about 45 seconds to fail with the exception noted above. I'd like it to give up sooner.
I did a little homework and it seems that you cannot adjust the DNS lookup timeout. So, I figured a better approach would be to an explicit DNS lookup so I could control it (and hopefully have the result cached to speed the next attempt). So that led me to the simple:
InetAddress addr = InetAddress.getByName(hostname);
but this also had a 45 second timeout. Others had mentioned that there was no control of the timeout for getByName(). Finally, I stumbled on a simple solution, just launch the lookup in a separate thread and manage your own timeout. This blog post shows this quite well.
private static boolean testDNS(String hostname) {
try
{
DNSResolver dnsRes = new DNSResolver(hostname);
Thread t = new Thread(dnsRes);
t.start();
t.join(1000);
InetAddress inetAddr = dnsRes.get();
return inetAddr != null;
}
catch(Exception e)
{
return false;
}
}
private static class DNSResolver implements Runnable {
private String domain;
private InetAddress inetAddr;
public DNSResolver(String domain) {
this.domain = domain;
}
public void run() {
try {
InetAddress addr = InetAddress.getByName(domain);
set(addr);
} catch (UnknownHostException e) {
}
}
public synchronized void set(InetAddress inetAddr) {
this.inetAddr = inetAddr;
}
public synchronized InetAddress get() {
return inetAddr;
}
}
Using this I can first test if the device can resolve the host name, then if it successful to the full connectivity test.