Android網絡監聽使用心得

關於監聽網絡變化的例子不少,但在使用過程當中,有些坑是須要注意的。本文目的是將問題暴露出來並提供解決思路。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 {
        ...
    }
    
}
接下來監聽Wifi網絡變化:
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

相關文章
相關標籤/搜索