12.1 問題
應用程序需要監(jiān)控網(wǎng)絡連接狀態(tài)的變化。
12.2 解決方案
(API Level 1)
通過ConnectivityManager監(jiān)控設備的網(wǎng)絡連接設備简珠。在移動應用程序的設計過程中梦湘,需要考慮的一個很重要的問題就是網(wǎng)絡并不是隨時都是連通的板乙。隨著人的移動,網(wǎng)絡的速度和容量都在不斷地變化。正因為如此蒋伦,使用網(wǎng)絡資源的應用程序需要隨時監(jiān)控這些資源是否可訪問,并不能訪問時通知用戶访敌。
除了連通性凉敲,ConnectivityManager還能向應用程序提供網(wǎng)絡連接的類型。這樣就能根據(jù)情況決定是否要下載大文件寺旺,如果用戶處于漫游狀態(tài)的話爷抓,下載大文件會使數(shù)據(jù)流量暴增。
12.3 實現(xiàn)機制
以下代碼清單封裝了一個方法阻塑,可以把它放到你的代碼中來檢查網(wǎng)絡的連通性蓝撇。
ConnectivityManager 封裝方法
public boolean isNetworkReachable() {
final ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo current = mManager.getActiveNetworkInfo();
if(current == null) {
return false;
}
return (current.getState() == NetworkInfo.State.CONNECTED);
}
ConnectivityManager所做的工作是評估哪個網(wǎng)絡數(shù)據(jù)接口被認為是活躍的(Wi-Fi或蜂窩網(wǎng)絡)。在最簡單的情況下陈莽,我們僅檢查給定接口是否已連接渤昌。注意:如果沒有可用的活躍數(shù)據(jù)連接,ConnectivityManager.getActiveNetworkInfo()會返回null走搁,所以首先要檢查這種情況独柑。如果有可用的網(wǎng)絡,就可以檢查其狀態(tài)私植,可能的返回值如下:
- DISCONNECTED
- CONNTECTING
- CONNECTED
- DISCONNECTING
如果返回的狀態(tài)是CONNECTED忌栅,網(wǎng)絡就是穩(wěn)定的,可以用來訪問遠程資源曲稼。
1. 驗證路線
移動設備有多條連接路線(Wi-Fi索绪、3G/4G等),并且設備經(jīng)常連接到?jīng)]有外部Web路線的網(wǎng)絡贫悄;Wi-Fi網(wǎng)絡尤其如此瑞驱。ConnectivityManager僅僅通知用戶其設備是否已關(guān)聯(lián)特定的網(wǎng)絡,但不會表明該網(wǎng)絡是否能夠訪問外部IP地址窄坦。因此唤反,當設備嘗試通過已“連接”但沒有有效路線的網(wǎng)絡進行連接時,網(wǎng)絡棧就會花費幾分鐘的時間超時并適當失敗鸭津。
在某些情況下彤侍,更加智能的方法是檢查有效的Internet連接,而非僅檢查與網(wǎng)絡的關(guān)聯(lián)曙博。以下代碼清單以前面的連通性檢查為基礎拥刻,用于檢查Internet連接怜瞒。
更加智能的ConnectivityManager封裝方法
public static boolean isNetworkReachable(Context context){
final ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
//如果沒有與網(wǎng)絡關(guān)聯(lián)父泳,則在此處進行關(guān)聯(lián)
boolean connected = (null != activeNetworkInfo && activeNetworkInfo.isConnected()){
if (!connected) return false;
//檢查是否可以訪問遠程服務器
boolean routeExists;
try{
//檢查Google公共DNS
InetAddress host = InetAddress.getByName("8.8.8.8");
Socket s = new Socket();
s.connect(new InetSocketAddress(host,53),5000);
//如果沒有拋出異常般哼,則DNS存在
routeExists = true;
s.close();
}catch (IOException e){
routeExists = false;
}
return (connected && routeExists);
}
如往常一樣驗證相同的連通性條件之后,上面的代碼清單進一步嘗試打開套接字(具有5秒的超時)惠窄,該套接字指向Google公共DNS的眾所周知的標準IPv4標準(8.8.8.8)蒸眠。如果成功連接此主機,我們就能夠確信設備可以訪問任何激活的Internet資源杆融。相比于直接全面連接遠程服務器楞卡,此方法的優(yōu)勢是代碼將更快地失敗,強制最多5秒的延遲脾歇,然后就立刻告訴用戶蒋腮,我們實際上并沒有自認為已具備的Internet連接。
當網(wǎng)絡請求失敗時藕各,最好檢查網(wǎng)絡的連通性池摧,告知用戶請求失敗是網(wǎng)絡問題。以下代碼清單中的示例演示了如何在網(wǎng)絡訪問失敗時檢查網(wǎng)絡的連通性激况。
告知用戶網(wǎng)絡不通
try{
//嘗試訪問網(wǎng)絡資源時作彤,如果失敗,可能會拋出HttpResponseException或其他 IOException
}catch(Exception e){
if( !isNetworkReachable() ){
AlertDialog.Builder builder = new AlertDialog.Builder builder(context);
builder.setTitle("No Network Connection");
builder.setMessage( " The Network is unavailable. "
+ " Please try your request again later. " );
builder.setPositiveButton("OK"乌逐,null);
builder.create().show();
}
}
2. 判斷連接類型
在某些情況下竭讳,應用程序還必須知道用戶所連接的網(wǎng)絡是否是按流量收費的,可以調(diào)用活動網(wǎng)絡連接的NetworkInfo.getType()方法以獲取相關(guān)信息(參見以下代碼)浙踢。
ConnectivityManager帶寬檢查
public boolean isWifiReachable(){
ConnectivityManager mManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo current = mManager.getActiveNetworkInfo();
if (current == null){
return false;
}
return (current.getType() == ConnectivityManager.TYPE_WIFI);
}
這個修改后的連通性檢查版本能夠判斷用戶是否連接到了Wi-Fi網(wǎng)絡绢慢,如果連接的是Wi-Fi網(wǎng)絡,通常就意味著網(wǎng)速比較快成黄,而且流量是免費的呐芥。