2

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));
}
Chewie
  • 125
  • 2
  • 7
  • We usually specify time out when initializing DefaultHttpClient to avoid app waiting server response forever. – yorkw Apr 17 '12 at 22:12
  • I edited my post to clarify that I actually see the request go out AND come back because of Charles, but the app doesn't do a thing! – Chewie Apr 18 '12 at 00:22
  • Are there any singleton classes being called and listeners being set for it that is also being called from somewhere else? Or is your spinner being dismissed in some callback function? – Shubhayu Apr 18 '12 at 01:32

4 Answers4

4

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();
  }
Mantas
  • 41
  • 2
1

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);

Anirudh
  • 2,524
  • 1
  • 14
  • 14
1

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();
IAmGroot
  • 13,760
  • 18
  • 84
  • 154
-1

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.

Thomas Dignan
  • 7,052
  • 3
  • 40
  • 48
  • Yes, it is called from an AsyncTask. The problem is that the code sometimes never gets past the .execute or .readValue – Chewie Apr 17 '12 at 22:10