如何在 Android 上检查互联网访问? InetAddress 永远不会超时

新手上路,请多包涵

我有一个 AsyncTask 应该检查对主机名的网络访问。但是 doInBackground() 永远不会超时。有人知道吗?

 public class HostAvailabilityTask extends AsyncTask<String, Void, Boolean> {

    private Main main;

    public HostAvailabilityTask(Main main) {
        this.main = main;
    }

    protected Boolean doInBackground(String... params) {
        Main.Log("doInBackground() isHostAvailable():"+params[0]);

        try {
            return InetAddress.getByName(params[0]).isReachable(30);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    protected void onPostExecute(Boolean... result) {
        Main.Log("onPostExecute()");

        if(result[0] == false) {
            main.setContentView(R.layout.splash);
            return;
        }

        main.continueAfterHostCheck();
    }
}

原文由 Vidar Vestnes 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 581
2 个回答

网络连接/互联网访问

  • isConnectedOrConnecting() (在大多数答案中使用)检查任何 网络 连接
  • 要了解这些网络中的任何一个是否可以访问 互联网,请使用以下方法之一

A)Ping 服务器(简单)

 // ICMP
public boolean isOnline() {
    Runtime runtime = Runtime.getRuntime();
    try {
        Process ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8");
        int     exitValue = ipProcess.waitFor();
        return (exitValue == 0);
    }
    catch (IOException e)          { e.printStackTrace(); }
    catch (InterruptedException e) { e.printStackTrace(); }

    return false;
}

+ 可以在主线程上运行

- 不适用于某些旧设备(Galays S3 等),如果没有互联网可用,它会阻塞一段时间。

B) 连接到 Internet 上的套接字(高级)

 // TCP/HTTP/DNS (depending on the port, 53=DNS, 80=HTTP, etc.)
public boolean isOnline() {
    try {
        int timeoutMs = 1500;
        Socket sock = new Socket();
        SocketAddress sockaddr = new InetSocketAddress("8.8.8.8", 53);

        sock.connect(sockaddr, timeoutMs);
        sock.close();

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

+ 非常快(无论哪种方式),适用于所有设备, 非常 可靠

- 无法在 UI 线程上运行

这在每台设备上都非常可靠,而且速度非常快。它需要在单独的任务中运行(例如 ScheduledExecutorServiceAsyncTask )。

可能的问题

  • 真的够快吗?

是的,非常快 ;-)

  • 除了在 Internet 上测试某些内容之外,是否没有可靠的方法来检查 Internet?

据我所知,但请告诉我,我会编辑我的答案。

  • 如果 DNS 宕机了怎么办?

Google DNS(例如 8.8.8.8 )是世界上最大的公共 DNS。截至 2018 年,它每天处理超过一万亿个查询 [ 1 ]。这么说吧,您的应用程序可能不会成为今天的话题。

  • 需要哪些权限?
   <uses-permission android:name="android.permission.INTERNET" />

只是互联网访问 - 惊喜 ^^(顺便说一句,你有没有想过,如果没有这个许可,这里建议的一些方法甚至可以远程连接互联网访问?)

额外:一次性 RxJava/RxAndroid 示例 (Kotlin)

 fun hasInternetConnection(): Single<Boolean> {
  return Single.fromCallable {
    try {
      // Connect to Google DNS to check for connection
      val timeoutMs = 1500
      val socket = Socket()
      val socketAddress = InetSocketAddress("8.8.8.8", 53)

      socket.connect(socketAddress, timeoutMs)
      socket.close()

      true
    } catch (e: IOException) {
      false
    }
  }
  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())
}

///////////////////////////////////////////////////////////////////////////////////
// Usage

    hasInternetConnection().subscribe { hasInternet -> /* do something */}

额外:一次性 RxJava/RxAndroid 示例(Java)

 public static Single<Boolean> hasInternetConnection() {
    return Single.fromCallable(() -> {
        try {
            // Connect to Google DNS to check for connection
            int timeoutMs = 1500;
            Socket socket = new Socket();
            InetSocketAddress socketAddress = new InetSocketAddress("8.8.8.8", 53);

            socket.connect(socketAddress, timeoutMs);
            socket.close();

            return true;
        } catch (IOException e) {
            return false;
        }
    }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}

///////////////////////////////////////////////////////////////////////////////////
// Usage

    hasInternetConnection().subscribe((hasInternet) -> {
        if(hasInternet) {

        }else {

        }
    });

额外:一次性 AsyncTask 示例

注意: 这显示了如何执行请求的另一个示例。然而,由于 AsyncTask 已被弃用,它应该被你的应用程序的线程调度、Kotlin Coroutines、Rx、…

 class InternetCheck extends AsyncTask<Void,Void,Boolean> {

    private Consumer mConsumer;
    public  interface Consumer { void accept(Boolean internet); }

    public  InternetCheck(Consumer consumer) { mConsumer = consumer; execute(); }

    @Override protected Boolean doInBackground(Void... voids) { try {
        Socket sock = new Socket();
        sock.connect(new InetSocketAddress("8.8.8.8", 53), 1500);
        sock.close();
        return true;
    } catch (IOException e) { return false; } }

    @Override protected void onPostExecute(Boolean internet) { mConsumer.accept(internet); }
}

///////////////////////////////////////////////////////////////////////////////////
// Usage

    new InternetCheck(internet -> { /* do something with boolean response */ });

原文由 Levite 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果设备处于飞行模式(或者可能在没有可用网络的其他情况下), cm.getActiveNetworkInfo() 将是 null ,因此您需要添加 null 查看。

修改( 埃迪的解决方案)如下:

 public boolean isOnline() {
    ConnectivityManager cm =
        (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo netInfo = cm.getActiveNetworkInfo();
    return netInfo != null && netInfo.isConnectedOrConnecting();
}

还要将以下权限添加到 AndroidManifest.xml

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

还有一点,如果您在给定的时间点绝对需要网络连接,那么最好使用 netInfo.isConnected() 而不是 netInfo.isConnectedOrConnecting 。我想这取决于个人用例。

原文由 gar 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题