Android互联网连接检查的更好方法

18
根据Android开发者网站上的确定和监测连接状态,我们可以检查是否存在活动的互联网连接。但是,即使只连接了Wi-Fi而没有可用的互联网(它会通知有互联网连接),这种方法也不起作用。
现在我通过ping一个网站来检查互联网连接是否可用。而这种方法需要更多的处理时间。是否有比这更好的方法来检查互联网连接性,以避免ping地址所需的时间延迟?

抱歉,它也可以使用Wifi。许多开发人员正在使用它,但仍然没有问题。即使我自己使用这个功能已经很多年了,对于互联网连接,我在使用WIFI或移动数据连接时也没有遇到任何问题。 - Andy Developer
1
是的,我也在使用同样的方法,即ping谷歌来检查互联网连接是否可用。是的,这需要一些时间,但我认为这是我们目前唯一的解决方案。 - Abdul Kawee
1
但是在设备上,如果wifi没有连接到互联网,我们可以看到(在wifi设置中)没有互联网文本。有没有可能获取这些数据?目前,我正在检查wifi设置的GitHub代码 :) - Rohith Krishnan
@RohithKrishnan,你能分享链接吗? - Abdul Kawee
2
请注意,仅仅对Google/Google DNS进行ping操作可能会导致误报,因为手机连接到了办公室/酒店/咖啡馆的访客Wi-Fi,这些请求都会被重定向到门户登录页面。更为可靠的方法是使用Microsoft的NCSI,您可以判断是否有实际的互联网连接,而不是门户登录页面。因此,请执行dns.msftncsi.com的DNS查找,验证其是否为131.107.255.255,或下载www.msftncsi.com并验证您是否获取到一个包含“Microsoft NCSI”的文本文件。 - Martheen
显示剩余5条评论
10个回答

10

试试这个:

它非常简单快速:

public boolean isInternetAvailable(String address, int port, int timeoutMs) {
    try {
        Socket sock = new Socket();
        SocketAddress sockaddr = new InetSocketAddress(address, port);

        sock.connect(sockaddr, timeoutMs); // This will block no more than timeoutMs
        sock.close();

        return true;

    } catch (IOException e) { return false; }
}

然后你可以在任何想要检查的地方使用这个:

if (isInternetAvailable("8.8.8.8", 53, 1000)) {
     // Internet available, do something
} else {
     // Internet not available
}

3
为什么您要检查Google DNS的可用性而不是您想要交互的实际网站?此外,原帖作者声称他们已经这样做了,并且正在寻找更好的方法。 - Dmitry Grigoryev
1
这是因为这样会更快。实际网站有时可能会因为流量过大而变慢。 - sumit
3
如果实际网站缓慢或无响应,与之交互的代码也会变得缓慢或无响应,即使连接速度检查很快。 - Dmitry Grigoryev
2
nop 用户的问题只是快速检查互联网是否可用。正如他在问题中提到的,他也在 ping 实际站点,但那种方法很慢。我提供的解决方案只是检查互联网是否可用。 - sumit
2
你在错误情况下忘记关闭套接字,如果你这样编码,你将面临用尽文件描述符的风险。 - Ferrybig
显示剩余2条评论

4
你需要搞清楚的第一个问题是,你所指的“互联网是否可用”是什么意思?
  • 未连接到WiFi或蜂窝网络;
  • 连接到有限制的WiFi:例如,在学校的网络中,如果你连接到学校的WiFi,可以直接访问内部网。但是,你必须使用学校帐户登录才能访问外部网。在这种情况下,如果你ping外部网站,则可能会收到响应,因为有些内部网站会自动重定向到登录页面;
  • 连接到无限制的WiFi:你可以自由地访问大多数网站;

第二个问题是你想要实现什么目标?
就我理解你的描述,你似乎想要测试网络连接并在失败时提醒用户。因此,我建议你只需ping你的服务器,这始终是有效的,如果你想与服务器交换数据。
你想知道是否有更好的方法来测试联通性,答案是否定的。
当前的TCP/IP网络是一种虚拟电路、分组交换的网络,这意味着数据没有固定的“路径”运行,即不像电话那样,我们有两个用户之间的真实连接,在电路断开后可以立即知道连接已丢失。我们必须向目标发送一个数据包,如果没有响应,我们就知道我们失去了连接(这就是ping -- ICMP协议 -- 的作用)。
总之,我们没有更好的方法来测试与主机的连接,除了ping它,这就是为什么“心跳”在服务管理中使用的原因。

@ Tony,我认为这个描述已经足够让你理解我现在面临的问题了。 - Rohith Krishnan
据我了解您的更新版本,您想测试与特定站点的连接状态,因此如果您想与该站点交换数据,那么没有更好的方法。 - Tony
是的,我找到了,并且还查看了Android WiFi设置的Google代码。 - Rohith Krishnan

4
尝试以下步骤:
public boolean checkOnlineState() {
    ConnectivityManager CManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo NInfo = CManager.getActiveNetworkInfo();
    if (NInfo != null && NInfo.isConnectedOrConnecting()) {
        return true;
    }
    return false;
}

别忘了访问权限:

 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

否则:
if (InetAddress.getByName("www.google.com").isReachable(timeout))
{    }
else
{    }

2

我想发表评论,但声望不够:/

无论如何,已接受答案存在一个问题,它不能捕获SocketTimeoutException异常,在野外(Android)中我见过这种情况导致崩溃。

public boolean isInternetAvailable(String address, int port, int timeoutMs) {
    try {
        Socket sock = new Socket();
        SocketAddress sockaddr = new InetSocketAddress(address, port);

        sock.connect(sockaddr, timeoutMs); // This will block no more than timeoutMs
        sock.close();

        return true;

    } catch (IOException e) { 
        return false; 
    } catch (SocketTimeoutException e) {
        return false;
    }
}

2
在检查此问题时,我们发现无法使用开发者网站中指定的方法确定是否存在活动的互联网连接: https://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html 这只会检查wifi的活动连接。
因此,我找到了两种方法来检查是否存在活动的互联网连接:
1.使用以下方法ping网站:
URL url = new URL(myUrl);
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        // 30 second time out.
        httpURLConnection.setConnectTimeout(30000);
        httpURLConnection.connect();
        if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
            isAvailable = true;
        }

2.使用socket检查Google DNS的可用性

  try {
        Socket sock = new Socket();
        SocketAddress sockaddr = new InetSocketAddress("8.8.8.8", 53);

        sock.connect(sockaddr, 1000); // this will block no more than timeoutMs
        sock.close();

        return true;
} 

第二种方法比第一种方法稍快(适合我的需求)
感谢大家的答案和支持。

2

//***检查互联网连接

public static Boolean isOnline(){

    boolean isAvailable = false;
    try {

        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);
        URL url = new URL("https://stackoverflow.com/");
        HttpURLConnection httpURLConnection = null;
        httpURLConnection = (HttpURLConnection) url.openConnection();

        // 2 second time out.
        httpURLConnection.setConnectTimeout(2000);
        httpURLConnection.connect();
        if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
            isAvailable = true;
        } else {
            isAvailable = false;
        }
    } catch (IOException e) {
        e.printStackTrace();
        isAvailable = false;
    }

    if (isAvailable){
        return true;
    }else {
        return false;
    }
}

1
请添加一些解释/内联代码注释来说明您的代码如何工作并解决OP的疑问。 - Madhur Bhaiya
如果(isOnline()){ 您的代码 } else{TOAST: ERROR_NOT_CONNECTION_TRY_AGAIN} - Milad shoeibi
如果返回 false 太晚,当您有 WiFi 信号但没有互联网连接时,您将无法弹出 toast。 - nafees ahmed

1
也许这可以帮助你:

private boolean checkInternetConnection() {
        ConnectivityManager cm = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
        // Test for connection
        if (cm.getActiveNetworkInfo() != null
            && cm.getActiveNetworkInfo().isAvailable()
            && cm.getActiveNetworkInfo().isConnected()) {
        return true;
    } 
    else {
        return false;
    }
}

2
这只能告诉您有一个工作的活动连接(wifi/移动数据),但不能确定该连接是否实际提供互联网访问。它可能连接到没有ISP连接的路由器,没有数据信用的移动数据,没有有效凭据的酒店wifi,受限制的学校/办公室局域网等。 - Martheen
然后,您应该使用Volley或任何其他方式进行HTTP请求,并根据响应处理响应。基于响应,您可以检查网络是否可用。 - callmejeevan

1

尝试这种方法,它会对你有所帮助:

public static boolean isNetworkConnected(Context context)
{
    ConnectivityManager connectivityManager = (ConnectivityManager)
            context.getSystemService(Context.CONNECTIVITY_SERVICE);
    if (connectivityManager != null)
    {
        NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
        if (netInfo != null && netInfo.isConnected())
        {
            return true;
        }
    }
    return false;
}

10
你没有理解他的观点,这种方法在Wi-Fi连接上没有互联网连接时即使是有限连接也会返回“true”。仔细阅读问题。 - Abdul Kawee

1
您可以尝试使用以下内容检查互联网连接:

/**
 * Check Connectivity of network.
 */
public static boolean isOnline(Context context) {
    try {
        if (context == null)
            return false;

        ConnectivityManager cm = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);

        if (cm != null) {
            if (cm.getActiveNetworkInfo() != null) {
                return cm.getActiveNetworkInfo().isConnected();
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    catch (Exception e) {
        Log.error("Exception", e);
        return false;
    }
}

在您的活动中,您可以像这样调用此函数。

if(YourClass.isOnline(context))
{
  // Do your stuff here.
}
else
{
  // Show alert, no Internet connection.
}

请不要忘记添加ACCESS_NETWORK_STATE权限:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

如果你只想ping这个URL,请尝试以下代码:

public static boolean isPingAvailable(String myUrl) {
    boolean isAvailable = false;
    try {
        URL url = new URL(myUrl);
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        // 30 second time out.
        httpURLConnection.setConnectTimeout(30000);
        httpURLConnection.connect();
        if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
            isAvailable = true;
        }
    } catch (Exception e) {
        isAvailable = false;
        e.printStackTrace();
    }
    return isAvailable;
}

1
不,它不能正常工作,我们需要ping任何网站来确认互联网的可用性。 - Rohith Krishnan
1
在这种情况下,您必须使用InetAddress。请查看此处 - Andy Developer

1

ConnectivityManager无法告诉您是否在WIFI上有活动连接。

检查是否有活动的互联网连接的唯一选项是ping URL。但是,您不需要在应用程序中进行每个HTTP请求时都这样做。

您可以这样做:

  1. Use below code to check connectivity

    private boolean checkInternetConnection()
    {
        ConnectivityManager cm = (ConnectivityManager) 
        getSystemService(Context.CONNECTIVITY_SERVICE);
        // test for connection
        if (cm.getActiveNetworkInfo() != null
            && cm.getActiveNetworkInfo().isAvailable()
            && cm.getActiveNetworkInfo().isConnected())
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
  2. And while making rest call using HTTP client set timeout like 10 seconds. If you don't get response in 10 seconds means you donot have active internet connection and exception will be thrown (Mostly you get response within 10 seconds). No need to check active connection by pinging everytime (if you are not making Chat or VOIP app)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接