Android-實時監聽網路狀態變化(觀察者)
實現流程概括
首先,我們要實現實時監聽,肯定要用到廣播機制:Android系統在網路狀態發生變化時會發送一條廣播,所以我們首先要做的就是寫一個廣播接收器,接收這條廣播。
那接收之後呢?
接收之後再通知所有的觀察者,網路有變化了,這裡就要用到觀察者模式了。(不知道觀察者模式也沒關係,其實就和回撥的機制差不多,不知道回撥是什麼的話......或許之後我會專門寫一篇?)
over,就這麼簡單。
1、工具類(準備考試)
開始之前,複製這幾個工具類到你的專案中,為後面的主要工作做準備。
首先是一個列舉,列舉的是幾種網路狀態。
public enum NetworkType { NETWORK_WIFI("WiFi"), NETWORK_4G("4G"), NETWORK_2G("2G"), NETWORK_3G("3G"), NETWORK_UNKNOWN("Unknown"), NETWORK_NO("No network"); private String desc; NetworkType(String desc) { this.desc = desc; } @Override public String toString() { return desc; } }
然後是一個網路工具類,用來返回網路連線狀態的型別。
public class NetworkUtil { private NetworkUtil() { throw new UnsupportedOperationException("u can't instantiate me..."); } @RequiresPermission("android.permission.ACCESS_NETWORK_STATE") private static NetworkInfo getActiveNetworkInfo(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); return cm.getActiveNetworkInfo(); } /** * 獲取當前網路型別 * 需新增許可權 {@code <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>} */ @RequiresPermission("android.permission.ACCESS_NETWORK_STATE") public static NetworkType getNetworkType(Context context) { NetworkType netType = NetworkType.NETWORK_NO; NetworkInfo info = getActiveNetworkInfo(context); if (info != null && info.isAvailable()) { if (info.getType() == ConnectivityManager.TYPE_WIFI) { netType = NetworkType.NETWORK_WIFI; } else if (info.getType() == ConnectivityManager.TYPE_MOBILE) { switch (info.getSubtype()) { case TelephonyManager.NETWORK_TYPE_TD_SCDMA: case TelephonyManager.NETWORK_TYPE_EVDO_A: case TelephonyManager.NETWORK_TYPE_UMTS: case TelephonyManager.NETWORK_TYPE_EVDO_0: case TelephonyManager.NETWORK_TYPE_HSDPA: case TelephonyManager.NETWORK_TYPE_HSUPA: case TelephonyManager.NETWORK_TYPE_HSPA: case TelephonyManager.NETWORK_TYPE_EVDO_B: case TelephonyManager.NETWORK_TYPE_EHRPD: case TelephonyManager.NETWORK_TYPE_HSPAP: netType = NetworkType.NETWORK_3G; break; case TelephonyManager.NETWORK_TYPE_LTE: case TelephonyManager.NETWORK_TYPE_IWLAN: netType = NetworkType.NETWORK_4G; break; case TelephonyManager.NETWORK_TYPE_GSM: case TelephonyManager.NETWORK_TYPE_GPRS: case TelephonyManager.NETWORK_TYPE_CDMA: case TelephonyManager.NETWORK_TYPE_EDGE: case TelephonyManager.NETWORK_TYPE_1xRTT: case TelephonyManager.NETWORK_TYPE_IDEN: netType = NetworkType.NETWORK_2G; break; default: String subtypeName = info.getSubtypeName(); if (subtypeName.equalsIgnoreCase("TD-SCDMA") || subtypeName.equalsIgnoreCase("WCDMA") || subtypeName.equalsIgnoreCase("CDMA2000")) { netType = NetworkType.NETWORK_3G; } else { netType = NetworkType.NETWORK_UNKNOWN; } break; } } else { netType = NetworkType.NETWORK_UNKNOWN; } } return netType; } }
最後,最重要的一點:
到manifast中新增訪問網路狀態的許可權
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
2、監聽廣播(劃重點,很可能考喔)
我們使用動態註冊的方式,所以首先新建一個 class ,命名為 NetStateChangeReceiver ,使其繼承自 BroadcastReceiver ,這樣一個廣播接收器就建好了。
public class NetStateChangeReceiver extends BroadcastReceiver{
當然,現在我們的接收器還不能接收任何東西,所以接下來,我們寫一個方法,告訴接收器接收網路變化的廣播。
public static void registerReceiver(Context context){ IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); context.registerReceiver( InstanceHolder.INSTANCE,intentFilter); }
IntentFilter 是一個過濾器,我們給他新增一個值為"android.net.conn.CONNECTIVITY_CHANGE"
的action,表示過濾的是網路改變廣播。
當呼叫 registerReceiver() 時,將 NetStateChangeReceiver 的例項和 IntentFilter 的例項都傳了進去,這樣 NetStateChangeReceiver 就會收到所有值為"android.net.conn.CONNECTIVITY_CHANGE"
的廣播,也就實現了監聽網路變化的功能。
有註冊當然也得有登出啦,在 unregisterReceiver() 方法中傳入之前註冊的廣播接收器例項,這個例項就登出掉了。
public static void unRegisterReceiver(Context context){ context.unregisterReceiver(InstanceHolder.INSTANCE); }
現在,每當網路變化時,onReceive() 方法就會自動被呼叫了,於是我們就可以重寫 onReceive() 方法,做點想♂做的事。
@Override public void onReceive(Context context, Intent intent) { if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())){ NetworkType networkType = NetworkUtil.getNetworkType(context); // do something } }
在這裡,我們拿到了網路連線型別 networkType,然後你就可以拿著這個狀態做你想做的事啦。
至此,網路監聽算是實現了。
3、觀察者(重難點,必考題)
(方便起見,我這裡只觀察網路的連線和斷開兩個變化)
講到觀察者,首先當然要建立觀察者啦,新建一個介面檔案 NetStateChangeObserver ,介面中有這兩個方法,一個是網路連線,一個是網路斷開。
void onNetDisconnected(); void onNetConnected(NetworkType networkType);
類似註冊登出接收器那樣,我們也需要新增移除觀察者。其實就是用一個 List 存放每個 observer ,新增 observer 時,就 add 到 List 裡面,移除 observer 時,就從 List 中刪掉對應的 observer。
private List<NetStateChangeObserver> mObservers = new ArrayList<>(); public static void registerObserver(NetStateChangeObserver observer){ if (!InstanceHolder.INSTANCE.mObservers.contains(observer)){ InstanceHolder.INSTANCE.mObservers.add(observer); } } public static void unRegisterObserver(NetStateChangeObserver observer){ InstanceHolder.INSTANCE.mObservers.remove(observer); }
終於到最後一步了,現在來寫通知所有observer 的方法。
把上面 onReceive() 方法中的 do something 改成通知所有 observer 。
NetworkType networkType = NetworkUtil.getNetworkType(context); // do something notifyObservers(networkType);
notifyObservers() 方法如下,型別變成無網路時就呼叫觀察者介面中的 onNetDisconnected() 方法,for 迴圈通知到每一個觀察者。型別為有網路時同理,略。
private void notifyObservers(NetworkType networkType){ if (networkType == NetworkType.NETWORK_NO){ for (NetStateChangeObserver observer : mObservers){ observer.onNetDisconnected(); } }else { for (NetStateChangeObserver observer : mObservers){ observer.onNetConnected(networkType); } } }
至此,觀察者也算是實現了。
4、怎麼在activity中使用(試卷一定要填姓名班級)
我們需要讓 activity 實現 NetStateChangeObserver 介面,然後在 onCreate 中註冊廣播接收,在 onDestroy 中登出接收器。這樣就能接收到網路變化的廣播了。
然後我們在 onResume 中新增觀察者,在 onPause 中移除觀察者。這樣就是當前activity成了一名觀察者。
最後重寫 NetStateChangeObserver 介面中的 onNetDisconnected 和 onNetConnected 方法,做一些你需要的事情,當有廣播時,這兩個方法中對應的方法就會自動被呼叫。
下面直接放出activity的程式碼,一看就懂,簡單粗暴。
public class MainActivity extends AppCompatActivity implements NetStateChangeObserver { @Override public void onNetDisconnected() { // do sth } @Override public void onNetConnected(NetworkType networkType) { // do sth } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate( savedInstanceState ); NetStateChangeReceiver.registerReceiver(this); } @Override protected void onDestroy() { NetStateChangeReceiver.unRegisterReceiver(this); super.onDestroy(); } @Override protected void onResume() { super.onResume( ); NetStateChangeReceiver.registerObserver(this); } @Override protected void onPause() { super.onPause( ); NetStateChangeReceiver.unRegisterObserver(this); } }
5、完整程式碼(檢查交卷,走人)
(上面講解部分省去了無關程式碼,所以請複製下面完整的)
工具類完整程式碼:
// 已經貼在最前面了。
NetStateChangeReceiver 類完整程式碼:
public class NetStateChangeReceiver extends BroadcastReceiver{ private NetworkType mType = NetworkUtil.getNetworkType(MyApplication.getContext()); private static class InstanceHolder{ private static final NetStateChangeReceiver INSTANCE = new NetStateChangeReceiver(); } private List<NetStateChangeObserver> mObservers = new ArrayList<>(); @Override public void onReceive(Context context, Intent intent) { if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())){ NetworkType networkType = NetworkUtil.getNetworkType(context); notifyObservers(networkType); } } public static void registerReceiver(Context context){ IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); context.registerReceiver( InstanceHolder.INSTANCE,intentFilter); } public static void unRegisterReceiver(Context context){ context.unregisterReceiver( InstanceHolder.INSTANCE); } public static void registerObserver(NetStateChangeObserver observer){ if (observer == null) { return; } if (!InstanceHolder.INSTANCE.mObservers.contains(observer)){ InstanceHolder.INSTANCE.mObservers.add(observer); } } public static void unRegisterObserver(NetStateChangeObserver observer){ if (observer == null) { return; } if (InstanceHolder.INSTANCE.mObservers == null) { return; } InstanceHolder.INSTANCE.mObservers.remove(observer); } private void notifyObservers(NetworkType networkType){ if (mType == networkType) { return; } mType = networkType; if (networkType == NetworkType.NETWORK_NO){ for (NetStateChangeObserver observer : mObservers){ observer.onNetDisconnected(); } }else { for (NetStateChangeObserver observer : mObservers){ observer.onNetConnected(networkType); } } } }
NetStateChangeObserver 介面完整程式碼:
public interface NetStateChangeObserver { void onNetDisconnected(); void onNetConnected(NetworkType networkType); }