關於監聽網絡變化的例子不少,但在使用過程當中,有些坑是須要注意的。本文目的是將問題暴露出來並提供解決思路。java
通常項目中須要監聽的無非就是移動網絡和wifi這兩種的變化。他們都是經過廣播來進行監聽。
動態註冊方式:網絡
IntentFilter filter = new IntentFilter(); //監聽wifi鏈接(手機與路由器之間的鏈接) filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); //監聽互聯網連通性(也就是是否已經能夠上網了),固然只是指wifi網絡的範疇 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); //這個是監聽網絡狀態的,包括了wifi和移動網絡。 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(new NetworkConnectChangedReceiver(), filter);
靜態註冊方式就不說了。但這裏第一個坑來了,靜態註冊時,Receiver的實例是由系統建立,所以,若是你的Receiver是內部類,那麼就會報錯:ide
java.lang.RuntimeException: Unable to instantiate receiver com.paulz.gametest.MainActivity$NetworkConnectChangedReceiver: java.lang.InstantiationException: can't instantiate class com.paulz.gametest.MainActivity$NetworkConnectChangedReceiver; no empty constructor
其實這個問題不算是網絡監聽的坑,這是java實例化對象時,對於內部類生命週期的問題,未用static修飾的內部類,其類對象初始化是在外部類的對象建立以後才加載到內存的。而廣播的註冊是由framework層所建立的,它是不會建立內部類廣播所在外部類的對象。,因此註冊廣播時,要麼別寫成內部類,要麼把他寫成靜態的。
注:若是不清楚類對象和類的對象,請自行查閱資料。code
public class A{ public static class NetworkConnectChangedReceiver extends BroadcastReceiver { ... } }
public static class NetworkConnectChangedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 這個監聽wifi的打開與關閉,與網絡連通性無關 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) { int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0); Log.e(TAG, "wifiState-" + wifiState); switch (wifiState) { case WifiManager.WIFI_STATE_DISABLED: Log.e(TAG, "wifiState------WIFI_STATE_DISABLED"); break; case WifiManager.WIFI_STATE_DISABLING: Log.e(TAG, "wifiState-----WIFI_STATE_DISABLING"); break; // } } // 監聽wifi的鏈接狀態,便是否連上了一個有效無線路由 if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) { Parcelable parcelableExtra = intent .getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); if (null != parcelableExtra) { NetworkInfo networkInfo = (NetworkInfo) parcelableExtra; NetworkInfo.State state = networkInfo.getState(); boolean isConnected = state == NetworkInfo.State.CONNECTED;// 固然,這邊能夠更精確的肯定狀態 Log.e(TAG, "isConnected" + isConnected); if (isConnected) { } else { } } } } }
監聽wifi的廣播響應速度比較快,若是隻監聽wifi的話,建議就用它。再而後就是監聽通用的網絡監聽廣播,網上的例子不少是用如下寫法,來斷定是否有網。但須要注意,這裏又有坑了。對象
如下代碼一樣是在onReceive方法中:生命週期
// 這個監聽網絡鏈接的設置,包括wifi和移動數據的打開和關閉。. // 最好用的仍是這個監聽。wifi若是打開,關閉,以及鏈接上可用的鏈接都會接到監聽 if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo gprs = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); Log.e(TAG, "網絡狀態改變:" + wifi.isConnected() + " 3g:" + gprs.isConnected()); NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); boolean hasNet = (info != null) && (info.isConnected()); if (info != null) { Log.e(TAG, "info.getTypeName()" + info.getTypeName()); Log.e(TAG, "getSubtypeName()" + info.getSubtypeName()); Log.e(TAG, "getState()" + info.getState()); Log.e(TAG, "getDetailedState()" + info.getDetailedState().name()); Log.e(TAG, "getDetailedState()" + info.getExtraInfo()); Log.e(TAG, "getType()" + info.getType()); if (NetworkInfo.State.CONNECTED == info.getState()) { //鏈接 } else if (info.getType() == ConnectivityManager.TYPE_WIFI) { if (NetworkInfo.State.DISCONNECTING == info.getState()) { } } } }
首先來看一下切換網絡時的過程。經過屢次試驗,發現有時候切換網絡的時候,系統會發屢次廣播,例如:
在已經連着3g網的狀況下,再鏈接wifi,用戶的正常思考邏輯是 斷3g-->連WiFi
但實際上系統發了超過2次廣播,並且順序不必定,好比本身實驗中,就是 斷3g-->連wifi-->連wifi-->斷3g。
因此按上面的代碼來看,即:內存
NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); boolean hasNet=info.isConnected();
結果會跟最後一次收到廣播時,判斷的網絡狀態爲準。
因此要做必定處理,由於網絡類型不單單隻有3g,wifi而已,好比4G等。因此最好的寫法以下:路由
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo[] infos=manager.getAllNetworkInfo(); int netType=-1; for(int i=0,length=infos.length;i<length;i++){ NetworkInfo info=infos[i]; if(info!=null&&info.isConnected()){ hasNet=true; netType=info.getType(); if (netType==ConnectivityManager.TYPE_MOBILE&&info.getSubtype()==TelephonyManager.NETWORK_TYPE_LTE) { netType=ConnectivityManager.TYPE_WIFI; } break; }else { hasNet=false; } }
雖然這種方式,每次接到廣播都要遍歷一次,但準確。
好了,具體用哪一種方式,仍是看狀況而定。get