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:
URL url = new URL(PATH);
URLConnection connection = url.openConnection();
HttpURLConnection httpUrlConnection = (HttpURLConnection) connection;
httpUrlConnection.setDoOutput(true);
httpUrlConnection.setDoInput(true);
httpUrlConnection.setUseCaches(false);
httpUrlConnection.setRequestProperty("Content-type", "application/x-java-serialized-object");
httpUrlConnection.setRequestMethod("POST");
httpUrlConnection.setConnectTimeout(3000);
httpUrlConnection.setReadTimeout(300);
httpUrlConnection.connect();
OutputStream outStrm = httpUrlConnection.getOutputStream();
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.
Using this I can first test if the device can resolve the host name, then if it successful to the full connectivity test.
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.