I have an issue in an app I developed - various users report that they are having issues with connectivity (only with my app). For example:
- Other apps seem to be able to access the internet, though mine cannot at the same time
- It only works over WiFi, but not over the mobile data connection.
My app connects via HttpUrlConnection (HTTPS) to a REST-based API.
A mistake of mine is that I don't really have a possibility to know which network errors occurred on a users phone (the error message says a generic 'No network'). I already implemented a limited retry to compensate for the issue, but I am at a loss what could be wrong otherwise.
My main network code is attached below, my questions to all experienced networking programmers:
- Is there something wrong with my code?
- Are the timeouts set realistically? (15sec for connect / read each)
- What kind of best practices are there to show or the cause of networking errors to the user or to diagnose it post-release?
-> Please NO advice "Use HTTPClient instead of HttpUrlConnection"
Code for creating a POST request:
public static HTTPURLResponseHolder doHTTPPost(URL url, List<NameValuePair> headers, String body, String charset, boolean followRedirect,
SSLContext context) throws IOException {
HttpURLConnection connection = getConfiguredConnection(url, headers, charset, context, true);
// WORKAROUND for recycling issue...
// http://stackoverflow.com/questions/19258518/
connection.setRequestProperty("Connection", "close");
connection.setRequestMethod("POST");
int contentLength = 0;
if (body != null) {
contentLength = body.getBytes().length;
}
try {
connection.setFixedLengthStreamingMode(contentLength);
BufferedOutputStream stream = null;
try {
OutputStream outputStream = null;
if (body != null) {
outputStream = connection.getOutputStream();
stream = new BufferedOutputStream(outputStream);
stream.write(body.getBytes());
stream.flush();
}
} finally {
close(stream);
}
InputStream inputStream = connection.getInputStream();
HTTPURLResponseHolder holder = new HTTPURLResponseHolder();
int responseCode = connection.getResponseCode();
holder.setResponseCode(responseCode);
// Normal case:
String o = StreamUtility.inputStreamToString(inputStream, charset);
close(inputStream);
holder.setOutput(o);
return holder;
} catch (IOException e) {
return handleIOException(url, headers, charset, false, context, connection);
} finally {
connection.disconnect();
}
}
static HttpURLConnection getConfiguredConnection(URL url, List<NameValuePair> httpHeaders, String charset, SSLContext context, boolean doOutput)
throws IOException {
if (url == null || url.getProtocol() == null || !url.getProtocol().toLowerCase(Locale.US).startsWith("http")) {
throw new IllegalArgumentException("Invalid HTTP URL: " + url);
}
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(15000);
connection.setReadTimeout(15000);
connection.setDoInput(true); // Read
if (doOutput) {
connection.setDoOutput(doOutput);
}
// Workaround for older Android versions who do not do that automatically (2.3.5 for example)
connection.setRequestProperty(HTTP.TARGET_HOST, url.getHost());
connection.setRequestProperty("Accept-Encoding", charset);
// Langauge
String language = Locale.getDefault().getLanguage();
if (StringUtility.isNullOrEmpty(language)) {
language = Locale.US.getLanguage();
}
connection.setRequestProperty("Accept-Language", language);
// Add the headers, if they exist
if (httpHeaders != null) {
for (NameValuePair nvp : httpHeaders) {
if (nvp != null) {
connection.setRequestProperty(nvp.getName(), nvp.getValue());
}
}
}
return connection;
}