Android HTTP Client freezes - android

I'm experiencing some odd behavior in my HTTP requests. I have some users that are saying that this call isn't ever coming back (the spinner marking it's asynchronous call never goes away). I have seen this happen before, but I attributed it to the emulator going through Charles Proxy. I haven't yet seen it on actual phone until now.
I'm not sure what would cause this to happen, which is why I'm posting it here. Here's the call, using Jackson to deserialize the result into a Value Object. The two spots I saw the emulator freeze are httpclient.execute(httpGet); and getObjectMapper().readValue(jp, SyncVO.class);.
While debugging, stepping over the offending statement caused the debugger to never gain control back of stepping. Meanwhile, I see the request go out AND come back from the server through Charles. It's just that the app doesn't seem to get the response and just sits there.
So, here's the code. Thanks for any help!
public SyncVO sync(String userId, long lastUpdate, boolean includeFetch) throws IOException {
SyncVO result = null;
String url = BASE_URL + "users/" + userId + "/sync" + "?" + "fetch=" + includeFetch;
if (lastUpdate > 0) {
url += "&updatedSince=" + lastUpdate;
}
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Accept", "application/json");
httpGet.setHeader("Accept-Encoding", "gzip");
httpGet.setHeader(AUTHORIZATION, BEARER + " " + mOAuthToken);
httpclient.getParams().setParameter(CoreProtocolPNames.USER_AGENT, USER_AGENT_STRING);
httpclient.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
HttpResponse response = httpclient.execute(httpGet);
if (isUnauthorized(response)) {
APPLICATION.needReauthentication();
return null;
}
if (response != null) {
InputStream stream = response.getEntity().getContent();
Header contentEncoding = response.getFirstHeader("Content-Encoding");
if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
stream = new GZIPInputStream(stream);
}
InputStreamReader inReader = new InputStreamReader(stream, "UTF-8");
JsonParser jp = mJsonFactory.createJsonParser(inReader);
result = getObjectMapper().readValue(jp, SyncVO.class);
}
return result;
}
private ObjectMapper getObjectMapper() {
return (new ObjectMapper()
.configure(Feature.AUTO_DETECT_FIELDS, true)
.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true));
}

don't forget to consume entities content after each request.
HttpEntity entity = response.getEntity();
try {
if (entity != null)
entity.consumeContent();
} catch (IOException e) {
e.printStackTrace();
}

You should definitely use connection timeout and socket read and be prepared for the worst from the server. Network operations will never be 100% predictable and there is not much your client can do then so make sure you code optimally.
httpParameters = httpclient.getParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, 5000);
HttpConnectionParams.setSoTimeout(httpParameters, 10000);
You can also cancel a task with asyncTask.cancel(true);

The reason is because you have left stream open. As such, the response is left in limbo. This means your global variable httpClient is also left in limbo, and unable to get a new entity when it re-uses the client.
You should call close() after finishing with the stream.
stream.close();

Network calls take a while and will block the UI thread. Same with your jackson deserialization code. This stuff needs to be put on a separate thread. See AsyncTask for an easy way to do it.

Related

Keep connection open and read data till it is forcefully closed

When my activity loads, I am connecting to a web service. As and when I get the response from service, I again call then service and so on.
#Override
protected void onCreate(Bundle savedInstanceState) {
….
callWebMethod();
}
// Called on getting response
#Override
public void run(String value) {
….
callWebMethod();
}
This is how I am connecting to service
HttpGet request = new HttpGet(url + combinedParams);
HttpClient client = new DefaultHttpClient(httpParameters);
HttpResponse httpResponse;
httpResponse = client.execute(request);
responseCode = httpResponse.getStatusLine().getStatusCode();
message = httpResponse.getStatusLine().getReasonPhrase();
HttpEntity entity = httpResponse.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
response = convertStreamToString(instream);
response = StringUtils.remove(response, "\n");
response = StringUtils.remove(response, '"');
}
Is it possible that I connect to the service only once at the start, then the connection remains open and application keeps on reading data from service till connection is forcefully closed.
Please let me know if more code is required.
Update: I then tried with ClientConnectionManager but still connection is again and again initialising. Though it is getting data. What I want is that connection remains open, and keeps on reading data from service.
HttpParams httpParameters = new BasicHttpParams();
SharedPreferences preferences = context.getSharedPreferences(
"MyPreferences", Context.MODE_PRIVATE);
int timeoutConnection = Integer.parseInt(preferences.getString(
"timeout", "60")) * 1000;
HttpConnectionParams.setConnectionTimeout(httpParameters,
timeoutConnection);
HttpConnectionParams.setSoTimeout(httpParameters, 2000);
System.setProperty("http.keepAlive", "true");
HttpClient client = new DefaultHttpClient(httpParameters);
ClientConnectionManager mgr = client.getConnectionManager();
client = new DefaultHttpClient(new ThreadSafeClientConnManager(
client.getParams(), mgr.getSchemeRegistry()),
client.getParams());
while (true) {
HttpResponse httpResponse;
try {
httpResponse = client.execute(request);
responseCode = httpResponse.getStatusLine().getStatusCode();
message = httpResponse.getStatusLine().getReasonPhrase();
HttpEntity entity = httpResponse.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
response = convertStreamToString(instream);
response = StringUtils.remove(response, "\n");
response = StringUtils.remove(response, '"');
((Activity) context).runOnUiThread(new Runnable() {
public void run() {
callback.run(response); // This calls activity callback function.
}
});
// Closing the input stream will trigger connection release
// instream.close();
}
} catch (ConnectTimeoutException e) {
….
}
It sounds like what you really need is a socket connection (see here). A socket will stay connected and allow you to stream data back and forth with the socket server until you are finished.
you just need to close the InputStream you get from HttpResponse.getEntity().getContent() after you are done using/reading-it. This will officially indicate the end of your current request.
You can then proceed to execute another request, the same HttpClient connection will be used.
Add a close
InputStream instream = entity.getContent();
response = convertStreamToString(instream);
// close the InputSream
instream.close()
// you can now reuse the same `HttpClient` and execute another request
// using same connection
httpResponse = client.execute(request);
Is it possible that I connect to the service only once at the start,
then the connection remains open...
The web server has a role to play in this. If the server "ends" the HTTP response, there is no further communication going to happen on same HTTP call.
It is possible to keep an HTTP connection open, with help of server. In this case, server never really ends the response but keeps writing data to response stream after some time intervals, so client can keep listening.
The new replacement for the above technique is a duplex socket connection. Both client and server can send and receive messages over a socket. Again, both client and server have to support it properly, and necessary handling for connection drops etc has to be there.
There are android specific client implementations available like https://github.com/nkzawa/socket.io-client.java that take care of most of connection management for you.
I think you could try to use the AsyncTask class to try to keep your thread open and do what you want, like this:
public class ConnectToWebService extends AsyncTask<Void, Void, Boolean> {
#Override
protected Boolean doInBackground(Void... params) { ... }
#Override
protected void onPostExecute(final Boolean success) { ... }
#Override
protected void onCancelled() { ... }
}
Check the API documentation for more information ;)

Android HttpDefaultClient times out only after another device makes a similar but not identical request

Some quick background. We have multiple devices running a scanner app which checks against a database to see whether an id has been scanned in or not. I can scan in with Device A as many times as I like without issue. I then pick up Device B and scan in, also as many or few times as I like. If I pick Device A back up and scan, the HttpClient will hang for approximately 60 seconds refusing to send any further requests. The code below has commented the point of failure.
// Asynchronous get request
private class aGETRequest extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String response = "";
for (String url : urls) {
DefaultHttpClient client = new DefaultHttpClient();
client.setKeepAliveStrategy(null);
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse httpResponse = client.execute(httpGet); //Hangs Here
HttpEntity httpEntity = httpResponse.getEntity();
InputStreamReader isr = new InputStreamReader(httpEntity.getContent());
BufferedReader buffer = new BufferedReader(isr);
String s = "";
while ((s = buffer.readLine()) != null) {
response += s;
}
httpEntity.consumeContent();
isr.close();
} catch (Exception e) {
e.printStackTrace();
}
httpGet.abort();
client.getConnectionManager().shutdown();
}
return response;
}
#Override
protected void onPostExecute(String result) {
results(result);
}
}
The client hangs and even snooping traffic shows no requests sent at all from Device A after the failure. You ready for the best part? If the devices are going through a Proxy server, it works. W.T.F?
Android is java 6 compat . right.
BufferedReader on java 7 makes me nervous and the while read loop appears to be whats hanging.....
I would try a different different read loop class thats solid on java 6 or i would find someone else's pattern for httpclient that's solid.
My wild guess is that your code is never getting out of the following...
while ((s = buffer.readLine()) != null)
Maybe the server is returing chunked encoding or something like that with a diff protocol ( pattern of length=0 followed by \r\n or something.

GZIP has no effect on web call

I've an app that call a webservice. I've logged the time it takes to complete this call with and without GZIP. I ran the app 5 times with and 5 time without GZIP and it actually took longer with GZIP. So i can only think GZIP had no effect or i have implemented it badly. Any ideas why there is no change?
public String connect(String url) {
HttpClient httpclient = new DefaultHttpClient();
// Prepare a request object
HttpGet httpget = new HttpGet(url);
httpget.addHeader("Accept-Encoding", "gzip");
// Execute the request
HttpResponse response;
try {
long start = System.currentTimeMillis();
response = httpclient.execute(httpget);
// Examine the response status
Log.i(TAG, response.getStatusLine().toString());
// Get hold of the response entity
HttpEntity entity = response.getEntity();
// If the response does not enclose an entity, there is no need
// to worry about connection release
if (entity != null) {
InputStream instream = response.getEntity().getContent();
Header contentEncoding = response.getFirstHeader("Content-Encoding");
if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
instream = new GZIPInputStream(instream);
}
// A Simple JSON Response Read
//InputStream instream = entity.getContent();
result = convertStreamToString(instream);
Log.i(TAG, result);
// A Simple JSONObject Creation
//json = new JSONObject(result);
// Closing the input stream will trigger connection release
instream.close();
long end = System.currentTimeMillis();
long elapsed = (end - start);
Log.e(TAG, "web call took ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" + elapsed);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
.
RESULTS:
Without GZIP: average of 5 runs = 2923ms
With GZIP: average of 5 runs = 3179ms
There are at least two major contributions in the timing:
client side: connection speed vs. decoding speed
server side: connection speed vs. encoding speed
The gzip encoding can be static or dynamic on the server side. For some content it would make sense to store query data in already compressed form. For some content it can't be done and the server may have the "compression engine" occupied.
The timings are likely to change between ADSL, WLAN or direct ethernet connections.

Android HttpURLConnection throwing EOFException

Here's the question in simplest way.
I create a HTTPS connection to my server through proxy using HttpUrlConnection Object.
My proxy closes the connection but my code still tries to reuse the same connection. And so I get EOFException.
How do I handle such cases?
I'd recommend disabling the http.keepalive system property. The performance section of the documentation indicates that socket connections will be reused when possible. It sounds like it is trying to reuse the connection, but the proxy has already closed it. On this post, Darrell also indicates that changing the system property solved his problem.
System.setProperty("http.keepAlive", "false");
Turns out they've fixed this issue in Android on Jan 8th [1]. The fix basically marks the old connection as recycled and internally retries the request.
To fix this for now, I would suggest retrying requests if an EOFException is encountered with a retry limit to prevent stackoverlows.
https://android.googlesource.com/platform/libcore/+/19aa40c81c48ff98ccc7272f2a3c41479b806376
I had this problem with normal HTTP connections as well. The first request went OK, the second failed with an EOFException.
Eventuelly I fixed it by removing...
urlConnection.setChunkedStreamingMode(0);
...from the HttpUrlConnection.
I could be that the webserver I'm calling can't handle chuncked data well. Don't know.
If you don't want to reuse the connection then release it.
finally {
urlConnection.disconnect();
}
You can use this method to pick data from server then you convert the inputs trim to string then you can parse for further use.`
public static InputStream getUrlData(final String url)
throws URISyntaxException, ClientProtocolException, IOException {
final DefaultHttpClient client = new DefaultHttpClient();
final HttpGet method = new HttpGet(new URI(url));
final HttpResponse res = client.execute(method);
return res.getEntity().getContent();
}
Maybe httpClient "has more bugs" and is deprecated, but this problem with JellyBean is a showstopper. I am using Ksoap2 so I tried all the suggested answers that I could.
System.setProperty("http.keepAlive", "false");
httpTransportSE.getServiceConnection().setRequestProperty("Connection", "close");
httpTransportSE.getServiceConnection().disconnect();
Nothing worked - my solution was to rollback the version of Ksoap2 I'm using from 3.1.1 to 2.6.5. Using 2.6.5 the problem is substantially reduced. Still testing but maybe even solved.
I found that retrying the connection fixes the issue as seen here: https://stackoverflow.com/a/20302767/2520390
Make sure you close off the connection before your recursive call.
Also, I added the following to the connection to close the connection, though I'm not sure if it helps:
if (retries > 0) {
connection.setRequestProperty("Connection", "close");
}
You shouldn't be attempting to reuse the same HttpURLConnection instance. The docs in the very bottom line of the "Class Overview" say
Each instance of HttpURLConnection may be used for one
request/response pair.
Keep-Alive connections work at a different level, see the disconnect docs:
http://developer.android.com/reference/java/net/HttpURLConnection.html#disconnect()
Unlike other Java implementations, this will not necessarily close
socket connections that can be reused. You can disable all connection
reuse by setting the http.keepAlive system property to false before
issuing any HTTP requests.
So you should always use a fresh HttpURLConnection and let the socket pool handle re-use.
There were apparently bugs with keep-alive connections pre-Froyo (2.2) so it is recommended to disable keep-alive on those old devices.
In my case the EOFException was caused by my server not sending a full response, see the details here: https://stackoverflow.com/a/27845172/2335025
You shouldn't be attempting to reuse the same HttpURLConnection instance. The docs in the very bottom line of the "Class Overview" say
Each instance of HttpURLConnection may be used for one
request/response pair.
Keep-Alive connections work at a different level, see the disconnect docs:
http://developer.android.com/reference/java/net/HttpURLConnection.html#disconnect()
Unlike other Java implementations, this will not necessarily close
socket connections that can be reused. You can disable all connection
reuse by setting the http.keepAlive system property to false before
issuing any HTTP requests.
So you should always use a fresh HttpURLConnection and let the socket pool handle re-use. There are perhaps issues if it tries to reuse a socket that has been closed by the server, which the answers to this question deal with: Android HttpUrlConnection EOFException
There were apparently bugs with keep-alive connections pre-Froyo (2.2) so it is recommended to disable keep-alive on those old devices.
In my case the EOFException was caused by my server not sending a full response, see the details here: https://stackoverflow.com/a/27845939/2335025
if (Build.VERSION.SDK != null
&& Build.VERSION.SDK_INT > 13) {
con.setRequestProperty("Connection", "close");
}
Try this code:`
Httppost method:
HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_MILLISEC);
HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_MILLISEC);
HttpClient client = new DefaultHttpClient(httpParams);
HttpPost request = new HttpPost("put_url");
request.setHeader("Content-Type", "application/xml");
String file = resourceXml();
StringEntity se = null;
try {
se = new StringEntity(file);
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
se.setContentEncoding("UTF-8");
se.setContentType("application/xml");
request.setEntity(se);
HttpResponse response = null;
try {
response = client.execute(request);
} catch (ClientProtocolException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
HttpEntity entity = response.getEntity();
InputStream is = null;
try {
is = entity.getContent();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String _response = convertStreamToString(is);
Log.i(TAG, "Response:" + _response);
// Check if server response is valid code
int res_code = response.getStatusLine().getStatusCode();
Log.i(TAG, "status_code" + res_code);
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
`
to convert stream to string:`
private static String convertStreamToString(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is),
8192);
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append((line + "\n"));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}`

Do POST request with Android DefaultHTTPClient cause freeze on execute()

I need post data to server.
I use this code:
HttpClient client = new DefaultHttpClient();
try {
HttpPost httppost = new HttpPost(serverUrl);
StringEntity se = new StringEntity(data);
httppost.setEntity(se);
httppost.setHeader("Accept", "application/json");
httppost.setHeader("Content-type", "application/json");
// Execute HTTP Post Request
HttpResponse response = client.execute(httppost);
int statusCode = response.getStatusLine().getStatusCode();
Log.i(TVProgram.TAG, "ErrorHandler post status code: " + statusCode);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (client != null) {
client.getConnectionManager().shutdown();
}
}
But problem is that Android freeze on execute() method, application is blocked out and after some time Android tell me that application doesn't respond.
I tried to debug into SDK classes and it freeze in AbstractSessionInputBuffer class on the line 103 which is
l = this.instream.read(this.buffer, off, len);
I also tried it run the request in separated thread, but the same problem.
I tested it on Android 2.1 (emulator) and Android 2.2 real mobile device.
I also tried to set HTTP proxy and use Fiddler to check HTTP communication data are received by server and server also send correct answer and HTTP code 200. All seems to be ok.
What is wrong please?
UPDATE: When I use AndroidHttpClient which is part of Android 2.2 SDK it works great. But it is not in earlier version of Android. So I include it's source code in my app for now. But AndroidHttpClient use DefaultHTTPClient internally, so problem will be in configuration of DefaultHttpClient.
I am using a POST HTTP request successfully. Here is my code. I removed pieces using handler to display messages etc. and the handler itself.
The POST string is like "&NAME=value#NAME2=value2"...
protected class ConnectingThread implements Runnable
{
Message msg;
private Handler mExtHandler;
private String mData;
private String mUrl;
/**
* #param h (Handler) - a handler for messages from this thread
* #param data (String) - data to be send in HTTP request's POST
* #param url (String) - URL to which to connect
*/
ConnectingThread(Handler h, String data, String url) {
mExtHandler = h;
mData = data;
mUrl = url;
}
public void run() {
try {
// TODO use the handler to display txt info about connection
URL url = new URL(mUrl);
URLConnection conn = url.openConnection();
conn.setConnectTimeout(CONN_TIMEOUT_MILLIS);
conn.setDoOutput(true);
BufferedOutputStream wr = new BufferedOutputStream(conn.getOutputStream());
wr.write(mData.getBytes());
wr.flush();
wr.close();
String sReturn = null;
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
int length = conn.getContentLength();
char[] buffer = new char[length];
int read = rd.read(buffer);
if(read == length)
sReturn = new String(buffer);
rd.close();
buffer = null;
// TODO use the handler to use the response
} catch (Exception e) {
//....
}
// TODO use the handler to display txt info about connection ERROR
}
}
Isn't client.execute(httppost); synchronous ?
You probably need to put this in a thread, else it will freeze the UI.
Yes it is being freezed just becoz you haven't implemented this as Asynchronous process. Because while it makes web request, your UI will wait for the response and then it will be updated once the response is received.
So this should be implemented as Asynchronous process, and user should be notified (with progress bar or progress dialog) that there is something happening.
Now, Instead of implementing Runnable class, in android its preferrable and recommended to use AsyncTask, its also known as Painless Threading.
Do you background tasks inside the doInBackground() method.
Do your display type of operations inside onPostExecute() method, like updating listview with fetched data, display values inside TextViews....etc.
Display ProgressBar or ProgressDialog inside the onPreExecute() method.
Use AndroidHttpClient helped me in this situation.
But now complete AndroidHttpClient and DefaultHttpClient are obsolete in current version of Android so it is not important now.

Categories

Resources