网络(移动 - > Wifi)更改时维护SocketConnection的最佳实践

时间:2011-10-24 09:03:37

标签: android

我有一个Android应用程序,它使用套接字连接连接到服务器,该应用程序处于活动状态时保持打开状态。如果手机处于非活动状态(锁定屏幕)或用户按下主页按钮,应用程序将关闭套接字连接并在应用再次可见时重新打开它。

这种模式在我们拥有的大多数Android手机(约15台设备)上运行良好,但摩托罗拉Milestone,Defy,SE Xperia Arc和LG Optimus One需要很长时间(> 10秒)来检测是否有Wifi在连接后可用。因此,为了等待最佳网络连接,我使用以下代码(在打开服务器的套接字之前):

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

和这个方法(由上面的方法使用)来获得测试连接,如果有一个(没有必要连接)可用,它更喜欢Wifi-Connection:

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

这适用于大多数设备,但对于上面提到的设备,这经常会失败,这种方法告诉我使用移动网络连接,当我打开一个导致SocketException的套接字连接时,设备会切换连接类型使用非常通用的错误消息,因此我无法确定套接字连接是由此问题引起还是由于某些其他网络错误。

简单地执行重试也不能解决这个问题,因为这会破坏其他网络错误的处理,因为它需要很长时间才能检测到套接字超时(因为它被检查了两次)。

是否还有其他人遇到此问题(连接到Wifi的速度非常慢)并且有解决方案吗?

2 个答案:

答案 0 :(得分:1)

是的,这是一个棘手的问题。一种选择是使用BroadcastReceiver等待正确的网络状态广播。

如下所述:How to detect when WIFI Connection has been established in Android?

在这里:How can I monitor the network connection status in Android?

答案 1 :(得分:0)

有一个名为droidfu的项目有一个HTTP包装器,可以绕过wi-fi / 3g问题。

以下是BetterHttpRequestBase类代码的摘录:

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