我有一个android应用程序,它连接到一个服务器使用套接字连接保持开放,而应用程序是活跃的。如果手机处于非活动状态(锁屏)或用户按下home键,应用程序将关闭套接字连接,并在应用程序再次可见时重新打开该连接。
这种模式在我们拥有的大多数安卓手机(大约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连接:
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),并有解决方案吗?
是的,这是一个棘手的问题。一种选择是使用BroadcastReceiver
等待正确的网络状态广播。
如下所述:如何在Android中检测WIFI连接是否已经建立?
这里:如何在Android中监控网络连接状态?
有一个叫做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;
}