Android隨筆3:如何判斷網(wǎng)絡(luò)斷開

判斷手機(jī)沒網(wǎng)了半火,手機(jī)又有網(wǎng)了越妈,看起來應(yīng)該是個(gè)很基礎(chǔ)的功能,但是我總感覺實(shí)現(xiàn)起來有點(diǎn)別扭钮糖。

以前判斷網(wǎng)絡(luò)是否連接的時(shí)候是ping一下服務(wù)器梅掠,這個(gè)方法目前看還是萬無一失酌住,ping不通App就沒有數(shù)據(jù),除非服務(wù)器掛了阎抒。

try {
      //-c 3  ping3次酪我,-w 60  超時(shí)時(shí)間為60秒
      Process p = Runtime.getRuntime().exec("ping -c 3 -w 60 " + "服務(wù)器ip地址");
      return p.waitFor() == 0;
  } catch (Exception e) {
      e(e.toString(), TAG);
      return false;
  }

同時(shí)還使用了ConnectivityManager#getActiveNetworkInfo方法獲取聯(lián)網(wǎng)狀態(tài),比如用NetworkInfo#isConnected()判斷網(wǎng)絡(luò)是否連接且叁,用NetworkInfo#getType()判斷網(wǎng)絡(luò)連接的類型都哭。但是版本29之后NetworkInfo這個(gè)類被標(biāo)記過期了。

NetworkInfo deprecated

推薦使用ConnectivityManager中的NetworkCallback獲取網(wǎng)絡(luò)連接的相關(guān)信息逞带∑劢茫或者通過getNetworkCapabilityes或者getNetworkLinkProperties同步獲取。

之前獲取網(wǎng)絡(luò)變化狀態(tài)的方法是通過注冊(cè)廣播CONNECTIVITY_ACTION展氓,同樣被標(biāo)記過期


廣播 deprecated

同時(shí)推薦了另外三個(gè)方法穆趴,這個(gè)三個(gè)方法就不是通過廣播獲取網(wǎng)絡(luò)狀態(tài)了,而是通過接口回調(diào)的方式異步返回信息遇汞。

研究一下新方法應(yīng)該怎么用未妹。還是在ConnectivityManager 這個(gè)類中。


ConnectivityManager

ConnectivityManager就是用來告訴我們網(wǎng)絡(luò)的連接狀態(tài)空入,以及通知網(wǎng)絡(luò)連接發(fā)生了哪些變化络它。那么現(xiàn)在應(yīng)該如何判斷當(dāng)前的網(wǎng)絡(luò)狀態(tài)?ConnectivityManager#getType的注釋中給了一些提示歪赢。

/**
     * Reports the type of network to which the
     * info in this {@code NetworkInfo} pertains.
     * @return one of {@link ConnectivityManager#TYPE_MOBILE}, {@link
     * ConnectivityManager#TYPE_WIFI}, {@link ConnectivityManager#TYPE_WIMAX}, {@link
     * ConnectivityManager#TYPE_ETHERNET},  {@link ConnectivityManager#TYPE_BLUETOOTH}, or other
     * types defined by {@link ConnectivityManager}.
     * @deprecated Callers should switch to checking {@link NetworkCapabilities#hasTransport}
     *             instead with one of the NetworkCapabilities#TRANSPORT_* constants :
     *             {@link #getType} and {@link #getTypeName} cannot account for networks using
     *             multiple transports. Note that generally apps should not care about transport;
     *             {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED} and
     *             {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps} are calls that
     *             apps concerned with meteredness or bandwidth should be looking at, as they
     *             offer this information with much better accuracy.
     */
    @Deprecated
    public int getType() {
        synchronized (this) {
            return mNetworkType;
        }
    }

這個(gè)提到了如果想判斷當(dāng)前網(wǎng)絡(luò)連接的類型酪耕,可以用NetworkCapabilities#hasTransport以及NetworkCapabilities中的常量進(jìn)行判斷,隨后又說app中可以不關(guān)心傳輸方式(transport)是怎樣的轨淌,通過NetworkCapabilities#NET_CAPABILITY_NOT_METERED和NetworkCapabilities#getLinkDownstreamBandwidthKbps可以更準(zhǔn)確的獲取到我們想要的信息迂烁。

其中g(shù)etLinkDownstreamBandwidthKbps就是字面意思,下載的帶寬是多少递鹉,個(gè)人認(rèn)為這也是判斷網(wǎng)絡(luò)是否連接的標(biāo)準(zhǔn)盟步,有數(shù)據(jù)回來了網(wǎng)絡(luò)就連接好了。有時(shí)連接到了路由器躏结,但是網(wǎng)絡(luò)欠費(fèi)了却盘,狀態(tài)是連接成功,實(shí)際上確沒網(wǎng)媳拴。

另外提到的NET_CAPABILITY_NOT_METERED黄橘,這個(gè)常量代表了是否是付費(fèi)網(wǎng)絡(luò),比如手機(jī)連接的是wifi屈溉,那么就是not metered塞关,未計(jì)量、不收費(fèi)的子巾,如果連接的是運(yùn)營(yíng)商的移動(dòng)網(wǎng)絡(luò)帆赢,那么就是metered小压,計(jì)量的、收費(fèi)的椰于。

最終判斷網(wǎng)絡(luò)是否連接的方式是怠益,是否有數(shù)據(jù)傳輸回來:

NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork());
int downstreamBandwidthKbps = networkCapabilities.getLinkDownstreamBandwidthKbps();
return downstreamBandwidthKbps > 0;

如果此時(shí)網(wǎng)絡(luò)不是連通狀態(tài),那么獲取到的networkCapabilities直接就是null瘾婿。

接下來看看如何監(jiān)聽網(wǎng)絡(luò)狀態(tài)變化蜻牢,文檔里推薦了三個(gè)方法,其中requestNetwork方法需要Manifest.permission.CHANGE_NETWORK_STATE權(quán)限偏陪,或者是修改系統(tǒng)設(shè)置(android.provider.Settings.System)孩饼。修改系統(tǒng)設(shè)置很麻煩,debug模式無法設(shè)置竹挡,需要打包并給apk系統(tǒng)簽名镀娶,然后跳轉(zhuǎn)到一個(gè)修改系統(tǒng)頁面手動(dòng)允許。

我用手里的華為手機(jī)測(cè)試時(shí)揪罕,設(shè)置了CHANGE_NETWORK_STATE權(quán)限梯码,就不需要修改系統(tǒng)設(shè)置了,官方文檔對(duì)于這兩種操作的說明也是用“or”進(jìn)行連接的好啰,所以暫時(shí)就認(rèn)為兩種操作取其一就可以了轩娶,如果其他手機(jī)既需要權(quán)限又需要修改系統(tǒng)設(shè)置,可能也可能框往。

registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback)方法就稍微省事一些鳄抒,不涉及修改系統(tǒng)設(shè)置,只需要考慮如何設(shè)置NetworkRequest就行了椰弊,比如要監(jiān)聽移動(dòng)網(wǎng)絡(luò)和wifi许溅。

NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addTransportType(TRANSPORT_WIFI);
builder.addTransportType(TRANSPORT_CELLULAR);
NetworkRequest request = builder.build();

如果還是覺得麻煩,request也不想設(shè)置秉版,因?yàn)檫€得弄明白request的參數(shù)都代表什么贤重,可以直接用registerDefaultNetworkCallback(NetworkCallback networkCallback),監(jiān)聽系統(tǒng)默認(rèn)的network清焕,參數(shù)只有callback回調(diào)并蝗,不需要對(duì)請(qǐng)求進(jìn)行設(shè)置,也可以監(jiān)聽移動(dòng)網(wǎng)絡(luò)和wifi的變化秸妥。

網(wǎng)絡(luò)連接有變化時(shí)會(huì)在NetworkCallback回調(diào)中告知我們滚停,NetworkCallback中的方法有:

mConnectivityManager.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback(){
                                    
        @Override
        public void onAvailable(Network network) {
              super.onAvailable(network);
              //連接準(zhǔn)備就緒時(shí)調(diào)用
        }

        @Override
        public void onLosing(Network network, int maxMsToLive) {
                super.onLosing(network, maxMsToLive);
                //斷開連接時(shí)調(diào)用。隨后會(huì)出現(xiàn)新的連接粥惧,緊接著觸發(fā)onAvailable
        }

        @Override
        public void onLost(Network network) {
                super.onLost(network);
                //斷開連接時(shí)調(diào)用键畴,徹底斷開,比如手動(dòng)關(guān)閉手機(jī)網(wǎng)絡(luò)影晓。
        }

        @Override
        public void onUnavailable() {
              super.onUnavailable();
              //未發(fā)現(xiàn)適用的網(wǎng)絡(luò)
        }

        @Override
        public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
              super.onCapabilitiesChanged(network, networkCapabilities);
              //Capabilities屬性改變時(shí)調(diào)用
        }

        @Override
        public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
              super.onLinkPropertiesChanged(network, linkProperties);
              //LinkProperties屬性改變時(shí)調(diào)用
        }

});

當(dāng)連接到新的網(wǎng)絡(luò)時(shí)镰吵,onAvailable檩禾,onCapabilitiesChanged挂签,onLinkPropertiesChanged疤祭,三個(gè)方法會(huì)依次出現(xiàn)。

在測(cè)試時(shí)發(fā)現(xiàn)饵婆,如果手動(dòng)斷開網(wǎng)絡(luò)勺馆,僅僅會(huì)調(diào)用onLoss方法,再此手動(dòng)打開手機(jī)網(wǎng)絡(luò)侨核,才會(huì)觸發(fā)onAvailable草穆。同時(shí),在源碼中發(fā)現(xiàn)了如下用法搓译。


callback

在onAvailable中記錄下當(dāng)前連接的NetWork悲柱,然后在onLost中判斷斷開的是否為當(dāng)前的連接的NetWork,如果是些己,則認(rèn)為網(wǎng)絡(luò)斷開豌鸡。

這里還有個(gè)細(xì)節(jié)需要弄清楚,手機(jī)是否一次只能連接一個(gè)Network段标,如果是涯冠,那么onAvailable和onLost組合才能正確判斷網(wǎng)絡(luò)是否連接。我在測(cè)試時(shí)逼庞,如果手機(jī)先連接了移動(dòng)網(wǎng)絡(luò)蛇更,在切換到Wi-Fi網(wǎng)絡(luò),會(huì)調(diào)用onAvailable方法赛糟,此時(shí)如果關(guān)閉Wi-Fi派任,會(huì)調(diào)用onLost方法,但此時(shí)手機(jī)為無網(wǎng)絡(luò)狀態(tài)(ping不通)璧南,并不會(huì)馬上切換回移動(dòng)網(wǎng)絡(luò)吨瞎,需要一小段時(shí)間才能切換回移動(dòng)網(wǎng)絡(luò)時(shí),然后重新觸發(fā)onAvailable方法穆咐。

所以判斷網(wǎng)絡(luò)斷開的方式就可以是在onAvailable中確認(rèn)網(wǎng)絡(luò)連接颤诀,在onLost中確認(rèn)網(wǎng)絡(luò)斷開:

public void onAvailable(Network network) {
        super.onAvailable(network);

        mCurrentNetwork = network;
        //網(wǎng)絡(luò)連接 do sth
    }

    @Override
    public void onLost(Network network) {
        super.onLost(network);

        ConnectivityManager conn = (ConnectivityManager) mContext.getSystemService(Activity.CONNECTIVITY_SERVICE);
        if (conn == null) {
            return;
        }

        if (network.equals(mCurrentNetwork)) {
            mCurrentNetwork = null;

            //延遲3s執(zhí)行,如果mCurrentNetwork仍為null对湃,那么可能沒有新的Network連接崖叫,則認(rèn)為網(wǎng)絡(luò)斷開
            Handler handler = new Handler();
            handler.postDelayed(() -> {
                if (mCurrentNetwork == null ) {
                    //網(wǎng)絡(luò)斷開 do sth
                }
            }, 3000);
        }

    }

Google之所依廢棄以前的方法,就是為了提供給我們更多拍柒,更準(zhǔn)確網(wǎng)絡(luò)信息心傀。比如在onCapabilitiesChanged和onLinkPropertiesChanged兩個(gè)回調(diào)方法中,把參數(shù)打印一下拆讯。
networkCapabilities.toString():

[ Transports: CELLULAR Capabilities: SUPL&INTERNET&NOT_RESTRICTED&TRUSTED&NOT_VPN&VALIDATED&FOREGROUND LinkUpBandwidth>=51200Kbps LinkDnBandwidth>=102400Kbps Specifier: <2>]

linkProperties.toString():


linkProperties.toString()

NetworkCapabilities脂男,描述了網(wǎng)絡(luò)的種類和帶寬养叛,原來描述手機(jī)網(wǎng)絡(luò)只需要一個(gè)常量TYPE_MOBILE,但是隨著手機(jī)網(wǎng)絡(luò)的速度越來快宰翅,4G弃甥、5G的發(fā)展,簡(jiǎn)單的分類不足以描述越來越多的手機(jī)網(wǎng)絡(luò)汁讼,于是Google采用了直接用帶寬描述網(wǎng)絡(luò)連接的方式淆攻,所以不直接給一個(gè)返回布爾值的方法判斷網(wǎng)絡(luò)連接狀態(tài)也沒啥好別扭的了。

LinkProperties 則包含地址嘿架、網(wǎng)關(guān)瓶珊、dns扳碍、代理等信息拧廊。

如果想知道具體的網(wǎng)絡(luò)種類撑螺,可以通過NetworkCapabilities#hasTransport方法荸型,通過不同的參數(shù)喷兼,可以判斷當(dāng)前的連接的網(wǎng)絡(luò)是什么馏予,參數(shù)有7種趾痘。

TRANSPORT_CELLULAR:蜂窩網(wǎng)絡(luò)
TRANSPORT_WIFI:wifi
TRANSPORT_BLUETOOTH:藍(lán)牙撥號(hào)逆济,手機(jī)通過藍(lán)牙連接pc上的網(wǎng)絡(luò)蜀肘,網(wǎng)上有教程
TRANSPORT_ETHERNET:因特網(wǎng)绊汹,手機(jī)直接連接網(wǎng)線,網(wǎng)上有這樣的轉(zhuǎn)接頭
TRANSPORT_VPN:vpn
TRANSPORT_WIFI_AWARE:這個(gè)沒見過扮宠,據(jù)說場(chǎng)景大概是西乖,比如你和一家商店通過這種協(xié)議進(jìn)行了網(wǎng)絡(luò)連接,下次你路過這家商店的時(shí)候坛增,手機(jī)會(huì)直接收到這家店的商品信息
TRANSPORT_LOWPAN:ipv6網(wǎng)絡(luò)

Google也提示我們不需要關(guān)心這些获雕,關(guān)注是否計(jì)費(fèi)和網(wǎng)絡(luò)帶寬就行。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末收捣,一起剝皮案震驚了整個(gè)濱河市届案,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌罢艾,老刑警劉巖楣颠,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異咐蚯,居然都是意外死亡童漩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門春锋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矫膨,“玉大人,你說我怎么就攤上這事〔嘞冢” “怎么了危尿?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)馁痴。 經(jīng)常有香客問我谊娇,道長(zhǎng),這世上最難降的妖魔是什么弥搞? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任邮绿,我火速辦了婚禮渠旁,結(jié)果婚禮上攀例,老公的妹妹穿的比我還像新娘。我一直安慰自己顾腊,他們只是感情好粤铭,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杂靶,像睡著了一般梆惯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吗垮,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天垛吗,我揣著相機(jī)與錄音,去河邊找鬼烁登。 笑死怯屉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饵沧。 我是一名探鬼主播锨络,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼狼牺!你這毒婦竟也來了羡儿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤是钥,失蹤者是張志新(化名)和其女友劉穎掠归,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悄泥,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡虏冻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了码泞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兄旬。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出领铐,到底是詐尸還是另有隱情悯森,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布绪撵,位于F島的核電站瓢姻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏音诈。R本人自食惡果不足惜幻碱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望细溅。 院中可真熱鬧褥傍,春花似錦、人聲如沸喇聊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽誓篱。三九已至朋贬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窜骄,已是汗流浹背锦募。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留邻遏,地道東北人糠亩。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像党远,于是被迫代替她去往敵國(guó)和親削解。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

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