Android-實(shí)時(shí)監(jiān)聽網(wǎng)絡(luò)狀態(tài)變化(觀察者)

寫在前面:

直接復(fù)制 1 的工具類和 5 的兩個(gè)類到項(xiàng)目中宏怔,就能使用坊谁,如果你不想看中間的過程的話。就是這么貼心的喔~


實(shí)現(xiàn)流程概括

首先矮嫉,我們要實(shí)現(xiàn)實(shí)時(shí)監(jiān)聽,肯定要用到廣播機(jī)制:Android系統(tǒng)在網(wǎng)絡(luò)狀態(tài)發(fā)生變化時(shí)會(huì)發(fā)送一條廣播牍疏,所以我們首先要做的就是寫一個(gè)廣播接收器蠢笋,接收這條廣播。

那接收之后呢鳞陨?

接收之后再通知所有的觀察者昨寞,網(wǎng)絡(luò)有變化了,這里就要用到觀察者模式了厦滤。(不知道觀察者模式也沒關(guān)系编矾,其實(shí)就和回調(diào)的機(jī)制差不多,不知道回調(diào)是什么的話......或許之后我會(huì)專門寫一篇馁害?)

over窄俏,就這么簡單。

1碘菜、工具類(準(zhǔn)備考試)

開始之前凹蜈,復(fù)制這幾個(gè)工具類到你的項(xiàng)目中,為后面的主要工作做準(zhǔn)備忍啸。

首先是一個(gè)枚舉仰坦,列舉的是幾種網(wǎng)絡(luò)狀態(tài)。

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;
    }
}

然后是一個(gè)網(wǎng)絡(luò)工具類计雌,用來返回網(wǎng)絡(luò)連接狀態(tài)的類型悄晃。

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();
    }

    /**
     * 獲取當(dāng)前網(wǎng)絡(luò)類型
     * 需添加權(quán)限 {@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;
    }
}

最后,最重要的一點(diǎn):

到manifast中添加訪問網(wǎng)絡(luò)狀態(tài)的權(quán)限

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

2凿滤、監(jiān)聽廣播(劃重點(diǎn)妈橄,很可能考喔)

我們使用動(dòng)態(tài)注冊的方式,所以首先新建一個(gè) class 翁脆,命名為 NetStateChangeReceiver 眷蚓,使其繼承自 BroadcastReceiver ,這樣一個(gè)廣播接收器就建好了反番。

public class NetStateChangeReceiver extends BroadcastReceiver{

當(dāng)然沙热,現(xiàn)在我們的接收器還不能接收任何東西,所以接下來罢缸,我們寫一個(gè)方法篙贸,告訴接收器接收網(wǎng)絡(luò)變化的廣播。

public static void registerReceiver(Context context){
    IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
    context.registerReceiver( InstanceHolder.INSTANCE,intentFilter);
}

IntentFilter 是一個(gè)過濾器枫疆,我們給他添加一個(gè)值為 "android.net.conn.CONNECTIVITY_CHANGE" 的action爵川,表示過濾的是網(wǎng)絡(luò)改變廣播。

當(dāng)調(diào)用 registerReceiver() 時(shí)养铸,將 NetStateChangeReceiver 的實(shí)例和 IntentFilter 的實(shí)例都傳了進(jìn)去雁芙,這樣 NetStateChangeReceiver 就會(huì)收到所有值為 "android.net.conn.CONNECTIVITY_CHANGE" 的廣播,也就實(shí)現(xiàn)了監(jiān)聽網(wǎng)絡(luò)變化的功能钞螟。


有注冊當(dāng)然也得有注銷啦兔甘,在 unregisterReceiver() 方法中傳入之前注冊的廣播接收器實(shí)例,這個(gè)實(shí)例就注銷掉了鳞滨。

public static void unRegisterReceiver(Context context){
    context.unregisterReceiver(InstanceHolder.INSTANCE);
}

現(xiàn)在洞焙,每當(dāng)網(wǎng)絡(luò)變化時(shí),onReceive() 方法就會(huì)自動(dòng)被調(diào)用了拯啦,于是我們就可以重寫 onReceive() 方法澡匪,做點(diǎn)想♂做的事。

@Override
public void onReceive(Context context, Intent intent) {
    if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())){
        NetworkType networkType = NetworkUtil.getNetworkType(context);
        // do something
    }
}

在這里褒链,我們拿到了網(wǎng)絡(luò)連接類型 networkType唁情,然后你就可以拿著這個(gè)狀態(tài)做你想做的事啦。

至此甫匹,網(wǎng)絡(luò)監(jiān)聽算是實(shí)現(xiàn)了甸鸟。

3、觀察者(重難點(diǎn)兵迅,必考題)

(方便起見抢韭,我這里只觀察網(wǎng)絡(luò)的連接和斷開兩個(gè)變化)

講到觀察者,首先當(dāng)然要?jiǎng)?chuàng)建觀察者啦恍箭,新建一個(gè)接口文件 NetStateChangeObserver 刻恭,接口中有這兩個(gè)方法,一個(gè)是網(wǎng)絡(luò)連接扯夭,一個(gè)是網(wǎng)絡(luò)斷開鳍贾。

void onNetDisconnected();
void onNetConnected(NetworkType networkType);

類似注冊注銷接收器那樣,我們也需要添加移除觀察者交洗。其實(shí)就是用一個(gè) List 存放每個(gè) observer 贾漏,添加 observer 時(shí),就 add 到 List 里面藕筋,移除 observer 時(shí)纵散,就從 List 中刪掉對應(yīng)的 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);
    }

終于到最后一步了隐圾,現(xiàn)在來寫通知所有 observer 的方法伍掀。

把上面 onReceive() 方法中的 do something 改成通知所有 observer 。

NetworkType networkType = NetworkUtil.getNetworkType(context);
// do something
notifyObservers(networkType);

notifyObservers() 方法如下暇藏,類型變成無網(wǎng)絡(luò)時(shí)就調(diào)用觀察者接口中的 onNetDisconnected() 方法蜜笤,for 循環(huán)通知到每一個(gè)觀察者。類型為有網(wǎng)絡(luò)時(shí)同理盐碱,略把兔。

private void notifyObservers(NetworkType networkType){
    if (networkType == NetworkType.NETWORK_NO){
        for (NetStateChangeObserver observer : mObservers){
            observer.onNetDisconnected();
        }
    }else {
        for (NetStateChangeObserver observer : mObservers){
            observer.onNetConnected(networkType);
        }
    }
}

至此沪伙,觀察者也算是實(shí)現(xiàn)了。

4县好、怎么在activity中使用(試卷一定要填姓名班級)

我們需要讓 activity 實(shí)現(xiàn) NetStateChangeObserver 接口围橡,然后在 onCreate 中注冊廣播接收,在 onDestroy 中注銷接收器缕贡。這樣就能接收到網(wǎng)絡(luò)變化的廣播了翁授。

然后我們在 onResume 中添加觀察者,在 onPause 中移除觀察者晾咪。這樣就是當(dāng)前activity成了一名觀察者收擦。

最后重寫 NetStateChangeObserver 接口中的 onNetDisconnected 和 onNetConnected 方法,做一些你需要的事情谍倦,當(dāng)有廣播時(shí)塞赂,這兩個(gè)方法中對應(yīng)的方法就會(huì)自動(dòng)被調(diào)用。

下面直接放出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曹洽、完整代碼(檢查交卷鳍置,走人)

(上面講解部分省去了無關(guān)代碼,所以請復(fù)制下面完整的)

工具類完整代碼:

// 已經(jīng)貼在最前面了送淆。

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);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末税产,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子偷崩,更是在濱河造成了極大的恐慌辟拷,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阐斜,死亡現(xiàn)場離奇詭異衫冻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)谒出,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門隅俘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笤喳,你說我怎么就攤上這事为居。” “怎么了杀狡?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵蒙畴,是天一觀的道長。 經(jīng)常有香客問我呜象,道長膳凝,這世上最難降的妖魔是什么碑隆? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蹬音,結(jié)果婚禮上上煤,老公的妹妹穿的比我還像新娘。我一直安慰自己祟绊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布哥捕。 她就那樣靜靜地躺著牧抽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪遥赚。 梳的紋絲不亂的頭發(fā)上扬舒,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機(jī)與錄音凫佛,去河邊找鬼讲坎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛愧薛,可吹牛的內(nèi)容都是我干的晨炕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼毫炉,長吁一口氣:“原來是場噩夢啊……” “哼瓮栗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瞄勾,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤费奸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后进陡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體愿阐,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年趾疚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缨历。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡糙麦,死狀恐怖戈二,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情喳资,我是刑警寧澤觉吭,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站仆邓,受9級特大地震影響鲜滩,放射性物質(zhì)發(fā)生泄漏伴鳖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一徙硅、第九天 我趴在偏房一處隱蔽的房頂上張望榜聂。 院中可真熱鬧,春花似錦嗓蘑、人聲如沸须肆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豌汇。三九已至,卻和暖如春泄隔,著一層夾襖步出監(jiān)牢的瞬間拒贱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工佛嬉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逻澳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓暖呕,卻偏偏與公主長得像斜做,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子湾揽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情況下的生命周期:在用戶參與的情況下...
    AndroidMaster閱讀 3,044評論 0 8
  • 前言Android四大組件重要性已經(jīng)不言而喻了陨享,今天談?wù)劦氖茿ndroid中的廣播機(jī)制。在我們上學(xué)的時(shí)候钝腺,每個(gè)班級...
    hahaoop閱讀 850評論 0 5
  • android四大組件:Activity,Service,BroadcastReceiver和ContentPro...
    毹毹閱讀 2,444評論 0 9
  • 1.Android廣播機(jī)制概述 Android廣播分為兩個(gè)方面:廣播發(fā)送者和廣播接收者抛姑,通常情況下,Broadca...
    IT小魔女的故事閱讀 554評論 0 2
  • 前言 本來想寫一下廣播的艳狐,發(fā)現(xiàn)查閱后有整理的不錯(cuò)的定硝,只好轉(zhuǎn)載圖個(gè)簡便,日后好復(fù)習(xí)轉(zhuǎn)載:http://www.cnb...
    提升即效率閱讀 1,387評論 0 10