在 Android 开发中,如果涉及到网络请求,而网络未连通的情况下,应用就会崩溃闪退,在 DEBUG 的时候出现该情况还好,但如果在用户的手机中,用户就会认为是你的应用出 Bug 了。

应用崩溃

另外还会有一种情况,有时候当手机显示网络连接成功时,并不一定表示可以真的上网,比如肯德基或者一些酒店的无线网络时常需要登录验证才能使用,又比如刚启动路由器时虽然手机上会显示连接到路由器但实际上路由器还未初始化完成所以未能上网,甚至是网络欠费了而你依然能够连接到路由器,都会出现上面的情况。

因此在进行网络通讯前,就需要确认网络是否真正联通。

PING

在 PC 上,我们可以通过 ping 命令来判断网络是否联通,PING 全称为 Packet Internet Groper,即因特网包探索器,可以将它作为网络诊断工具,ping 命令在 Windows、macOS 和 Linux 下都是可以直接使用的,那 Android 当然也不例外:

public static boolean isNetPingUsable() {
    Runtime runtime = Runtime.getRuntime();
    try {
        Process process = runtime.exec("ping -c 3 www.baidu.com");
        int ret = process.waitFor();
        if (ret == 0) {
            return true;
        } else {
            return false;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return false;
}

使用 ping 命令可以获得不同网络状态下的结果,如下:

  • 连通的移动数据网络下:0
  • 需要网页认证的 Wi-Fi 下:1
  • Wi-Fi 打开但没有网络连接,数据也不可用的状态下:2
  • 在不可用的 Wi-Fi 下:2
  • 在可用的 Wi-Fi 下:0

所以只要判断结果为 0 即表示网络已连通。

但该方法有个致命的缺点,当网络未连通时,它可以马上返回 false,但是当网络连通时,它需要耗费约 10 秒的时间才能返回结果。『广东移动』的 Android App 早期版本在登录页面获取短信验证码的时候也出现过该情况,点击「获取验证码」按钮后会出现一个 ProgressDialog 等待约 10 秒后才进行获取请求,估计也是使用上面的这个方法。

NetworkCapabilities

NetworkCapabilities 是 API 21 新增加的一个工具类,这类本身没有太多内容,主要是用来描述网络状态和传输类型,它提供了相应的方法用来判断当前网络是否可用,并且不会出现上方的耗时操作。代码如下:

public static boolean isNetworkAvailable(Context context) {
    ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    if (connectivity != null) {
        NetworkInfo info = connectivity.getActiveNetworkInfo();
        if (info != null && info.isConnected()) {       // 当前网络是连接的
            return info.getState() == NetworkInfo.State.CONNECTED;      // 当前所连接的网络可用
        }
    }
    return false;
}

这样,当网络未连通的情况下,就可以马上获得结果,配合 ProgressDialogToast 提醒可以使交互更加友好。

断网提醒

建议在封装网络请求工具时加入联网判断,以防止 App 因网络问题崩溃。当然,现在很多第三方网络框架也已经封装了联网判断,可以直接使用。