3

I have an android app which connects to a server using a socket connection which is kept open while the app is active. If the phone gets inactive (lock screen) or the user presses the home button, the application closes the socket connection and reopens it if the app becomes visible again.

This Pattern works fine on most of the android phones we have (about 15 devices), but the Motorola Milestone, Defy, SE Xperia Arc and the LG Optimus One need very long (>10 secs) to detect if a Wifi is available after and connect to it. So to wait for the best network connection I use the following code (before opening the socket to the server):

public static boolean waitNetworkConnection(Context context, int retries) {
    ConnectivityManager cm = (ConnectivityManager) 
                       context.getSystemService(Context.CONNECTIVITY_SERVICE);

    NetworkInfo ni = getNetworkToTest(cm);

    if (ni == null || !ni.isConnected()) {
        // sleep a short while to allow system to switch connecting networks.
        Tools.sleep(1000);
        int counter = 0;
        while (counter < retries && (ni == null || (ni.isAvailable() && 
               !ni.isConnected()))) {
            Tools.sleep(500);
            ni = getNetworkToTest(cm);
            counter++;
        }
    }

    return (cm.getActiveNetworkInfo() != null && 
            cm.getActiveNetworkInfo().isConnected());
}

and this method (use by the one above) to get the connection to test, which prefers a Wifi-Connection if one (not necessary connected) is available:

private static NetworkInfo getNetworkToTest(ConnectivityManager cm) {
    NetworkInfo[] nis = cm.getAllNetworkInfo();
    NetworkInfo ni = cm.getActiveNetworkInfo();

    for (int i = 0; i < nis.length; i++) {
        if (nis[i].getType() == 1 /* Wifi */ && nis[i].isAvailable()) {
            ni = nis[i];
            return(ni);
        }
    }
    return(ni);
}

This works fine for most of the devices, but for the mentioned ones this very often fails and this method tells me to use a mobile network connection and the device switches the connection type while I open a socket connection which leads to a SocketException with a very generic error message so I'm unable to determine if the socket connection is caused by this issue or because of some other network error.

Simply doing a retry doesn't fix this either, as this breaks the handling for the other network errors because it then takes very long to detect a socket timeout (because it is checked twice).

Has anyone else ran into this problem (very slowing connect to Wifi) and has a solution for this?

HefferWolf
  • 3,894
  • 1
  • 23
  • 29

2 Answers2

1

Yes, this is a tricky problem. One option would be to wait for the right network state broadcast using a BroadcastReceiver.

As described here: How to detect when WIFI Connection has been established in Android?

And here: How can I monitor the network connection status in Android?

Community
  • 1
  • 1
Che Jami
  • 5,151
  • 2
  • 21
  • 18
0

There is a project called droidfu that has a HTTP wrapper, that gets round the wi-fi / 3g issue.

Here is a snippet from the code for the BetterHttpRequestBase class:

public BetterHttpResponse send() throws ConnectException {

    BetterHttpRequestRetryHandler retryHandler = new BetterHttpRequestRetryHandler(maxRetries);

    // tell HttpClient to user our own retry handler
    httpClient.setHttpRequestRetryHandler(retryHandler);

    HttpContext context = new BasicHttpContext();

    // Grab a coffee now and lean back, I'm not good at explaining stuff. This code realizes
    // a second retry layer on top of HttpClient. Rationale: HttpClient.execute sometimes craps
    // out even *before* the HttpRequestRetryHandler set above is called, e.g. on a
    // "Network unreachable" SocketException, which can happen when failing over from Wi-Fi to
    // 3G or vice versa. Hence, we catch these exceptions, feed it through the same retry
    // decision method *again*, and align the execution count along the way.
    boolean retry = true;
    IOException cause = null;
    while (retry) {
        try {
            return httpClient.execute(request, this, context);
        } catch (IOException e) {
            cause = e;
            retry = retryRequest(retryHandler, cause, context);
        } catch (NullPointerException e) {
            // there's a bug in HttpClient 4.0.x that on some occasions causes
            // DefaultRequestExecutor to throw an NPE, see
            // http://code.google.com/p/android/issues/detail?id=5255
            cause = new IOException("NPE in HttpClient" + e.getMessage());
            retry = retryRequest(retryHandler, cause, context);
        } finally {
            // if timeout was changed with this request using withTimeout(), reset it
            if (oldTimeout != BetterHttp.getSocketTimeout()) {
                BetterHttp.setSocketTimeout(oldTimeout);
            }
        }
    }

    // no retries left, crap out with exception
    ConnectException ex = new ConnectException();
    ex.initCause(cause);
    throw ex;
}
Robert
  • 37,670
  • 37
  • 171
  • 213
  • They just do a retry (which works fine if you are using stateless http), but I need a stateful socket connect. – HefferWolf Oct 24 '11 at 12:01