我們知道,當手機上顯示網絡連接成功時,并不一定可以真的上網。常見的情況是,連上路由后需要進行跳轉登錄,或者干脆路由連接到網絡的端口壞掉。因此在進行網絡通訊前,可能需要確認網絡是否真正聯通。
經典方法
一種常用的方法,也是沿用pc機上查看網絡是否聯通的方法,就是使用ping命令。要注意ping在windows上和linux上的參數意義是不一樣的。使用-c來確定ping的次數。adb shell進入android環境,輸入命令,效果如下。這里www.a.shifen.com是百度為了防止攻擊而設置的殼地址,可以忽略。
那么現在要做的就是在android代碼里使用ping命令,代碼如下:
Runtime runtime = Runtime.getRuntime();
try {
Process p = runtime.exec("ping -c 3 www.baidu.com");
int ret = p.waitFor();
Log.i("Avalible", "Process:"+ret);
} catch (Exception e) {
e.printStackTrace();
}
然后我們來看下不同網絡狀態下代碼的執行效果:
- 連通的移動數據網絡下:
- 在需要網頁認證的wifi下:
- 在wifi打開但沒有網絡連接,數據也不可用的狀態下:
- 在不可用wifi下:
- 在可用wifi下:
基本上只要判斷是否是0即可,若是0則網絡真正可用。
Android 6.0以上可用方法
在API 21增加了一個類NetworkCapabilities,其中有很多對于網絡性能的描述,可以通過ConnectivityManager獲得描述當前網絡的NetworkCapabilities。而在API23中,增加了一個描述NET_CAPABILITY_VALIDATED。文檔中對其描述如下:
Indicates that connectivity on this network was successfully validated. For example, for a network with NET_CAPABILITY_INTERNET, it means that Internet connectivity was successfully detected.
實驗表明,當NetworkCapabilities的描述中有VALIDATED這個描述時,此網絡是真正可用的。使用如下代碼獲得當前網絡的capabilities:
NetworkCapabilities networkCapabilities = mConnectivityManager.getNetworkCapabilities(mConnectivityManager.getActiveNetwork());
Log.i("Avalible", "NetworkCapalbilities:"+networkCapabilities.toString());
可以看到代碼非常簡單,將對于NetworkCapabilities的描述打印出來,不同網絡狀態下結果如下:
- 在可聯通的wifi下,可看到出現了VALIDATED的標記:
- 在需要認證的wifi下,可以看到相應區域沒有VALIDATED的標記:
- 在不可上網的wifi下,可以看到也沒有VALIDATED的標記:
- 在移動數據下,可以看到VALIDATED又出現了:
因此可以通過判斷這個標記是否存在來判斷網絡的連通性。通過如下調用來判斷標記存在與否:
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
可惜只能6.0以上使用,目前還是又很多6.0下的手機在使用的,因此經典方法還是很重要。
http協議
其實查看網絡連接是否成功的本質,就是實際去連下,看看能不能連到。所以理論上任何網絡通訊協議都可以來處理這件事。但實際上,開發中大多使用經典方法是有道理的。因為ping命令本身就是為了查看網絡連通狀況而存在。而使用http協議的話,不但數據包會較大、使用較復雜,很多情況也沒法處理。下面來簡單地用Get去訪問url看看會發生什么。代碼如下:
HttpURLConnection httpConn = null;
InputStream inputStream = null;
int respondcode = -1;
try{
URL url = new URL("http://www.baidu.com");
httpConn = (HttpURLConnection)url.openConnection();
httpConn.setRequestMethod("GET");
respondcode = httpConn.getResponseCode();
Log.i("Avalible", "HttpURLConnection1:"+respondcode+" "+httpConn.getHeaderField("Location"));
url = new URL(httpConn.getHeaderField("Location"));
httpConn = (HttpURLConnection)url.openConnection();
httpConn.setUseCaches(false);
httpConn.setRequestMethod("GET");
respondcode = httpConn.getResponseCode();
Log.i("Avalible", "HttpURLConnection2:"+respondcode+" "+httpConn.getHeaderField("Location"));
}catch(Exception e){
e.printStackTrace();
}
由于可能會發生重定向,因此進行兩次http請求,并且將重定向的url打印出來查看。不同網絡下結果如下:
- 移動數據流量。首先進行了一次跳轉,第二次返回200成功:
- 可連通wifi下,完全一樣:
- 需要認證wifi下,返回200,但實際上并沒有確認可連通www.baidu.com,只是連接到了需要登錄的地址。因此實際上是沒法通過返回值來判斷究竟能否上網的:
- 而在無法上網的情況下,是直接報錯的:
因此相對來說,使用http協議,處理的過程更加復雜,返回結果不夠明確,相對不適合用來判斷網絡是否連通。