我正在开发一个Android应用程序,它定期通过REST API调用报告回数据服务器。每当应用程序尝试发送其中一个报告,但没有可用的网络通道(wifi或移动数据)时,应用程序将为该任务创建一个包装器并将其放入附加到BroadcastReceiver
的队列中。接收方侦听android.net.wifi.STATE_CHANGE
或android.net.conn.CONNECTIVITY_CHANGE
,然后尝试在新连接上执行排队任务。此处也处理所有身份验证逻辑。实际的请求POST将通过Retrofit库发出。
我的问题是,每当网络访问"回来",并且接收者试图启动所有等待的任务时,我总是回到java.net.UnknownHostException
,说明主机数据服务器的名称没有关联的地址。看来,虽然Android设备知道它已连接到网络,但它仍然无法确定从哪里获取DNS,以及任何在此状态下发出的请求会失败。
D/Retrofit﹕ java.net.UnknownHostException: Unable to resolve host "[REDACTED]": No address associated with hostname
at java.net.InetAddress.lookupHostByName(InetAddress.java:424)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:236)
at java.net.InetAddress.getAllByName(InetAddress.java:214)
at com.squareup.okhttp.internal.Network$1.resolveInetAddresses(Network.java:29)
at com.squareup.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:266)
at com.squareup.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:240)
at com.squareup.okhttp.internal.http.RouteSelector.nextUnconnected(RouteSelector.java:156)
at com.squareup.okhttp.internal.http.RouteSelector.next(RouteSelector.java:130)
at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:312)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:235)
at com.squareup.okhttp.Call.getResponse(Call.java:262)
at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:219)
at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:192)
at com.squareup.okhttp.Call.execute(Call.java:79)
at retrofit.client.OkClient.execute(OkClient.java:53)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:326)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
at [REDACTED](Native Method)
at [REDACTED]
[etc]
Caused by: libcore.io.GaiException: getaddrinfo failed: EAI_NODATA (No address associated with hostname)
Caused by: libcore.io.ErrnoException: getaddrinfo failed: ENETUNREACH (Network is unreachable)
根本原因异常(libcore.io.ErrnoException
)声称网络无法访问,但在我收到意图之前,我甚至不尝试执行此操作来自Android OS ,特别是因为网络现在实际上是可以访问的。
我验证无线网络可用性的代码:
protected static boolean isWifiAvailable( WifiManager wifi )
{
WifiInfo info = wifi.getConnectionInfo() ;
if( info == null ) return false ;
if( info.getNetworkId() == -1 ) return false ;
if( info.getSupplicantState() != SupplicantState.COMPLETED )
return false ;
return true ;
}
我的验证移动数据可用性的代码:
protected static boolean isMobileDataAvailable( TelephonyManager tmgr )
{
return( tmgr.getDataState() == TelephonyManager.DATA_CONNECTED ) ;
}
问题:是否有更可靠的信号 - 我不会在WifiInfo
和TelephonyManager
中检查 - 这表明网络频道是真的,真的准备好了?或者这是一个没有明确解决方案的竞争条件?
答案 0 :(得分:1)
经常发生我在发布问题后不久就找到了解决方案。事实证明,移动数据检查足够......大部分时间。 wifi检查需要更多工作:
protected static boolean isWifiAvailable( WifiManager wifi )
{
WifiInfo info = wifi.getConnectionInfo() ;
if( info == null ) return false ;
if( info.getNetworkId() == -1 ) return false ;
if( info.getSupplicantState() != SupplicantState.COMPLETED )
return false ;
// Here are the crucial additions:
DhcpInfo dhcp = wifi.getDhcpInfo() ;
if( dhcp == null ) return false ;
if( dhcp.ipAddress != info.getIpAddress() ) return false ;
return true ;
}
通过WifiInfo
对DhcpInfo
进行检查,我们终于可以确定当前的无线网络是否可路由。