Android網(wǎng)絡(luò)狀態(tài)獲取及NetworkCallback簡(jiǎn)述

前言

在APP的開(kāi)發(fā)中,獲取到網(wǎng)絡(luò)的鏈接狀態(tài)是一個(gè)經(jīng)常使用到的方法聂薪。除了可以使用ping指令來(lái)判斷當(dāng)前的網(wǎng)絡(luò)狀況之外家乘,還可以直接通過(guò)ConnectivityManager來(lái)對(duì)網(wǎng)絡(luò)狀態(tài)進(jìn)行判斷。

一藏澳、網(wǎng)絡(luò)判斷舊方法(deprecated)

權(quán)限申請(qǐng)

如果要獲取網(wǎng)絡(luò)信息仁锯,首先是需要申請(qǐng)網(wǎng)絡(luò)權(quán)限:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
判斷網(wǎng)絡(luò)

使用ConnectivityManager可以十分方便的直接判斷網(wǎng)絡(luò),調(diào)用方法如下:

    //判斷網(wǎng)絡(luò)是否連接
    fun isNetworkConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val mNetworkInfo = mConnectivityManager.activeNetworkInfo
            if (mNetworkInfo != null) {
                return mNetworkInfo.isAvailable
            }
        }
        return false
    }
        //判斷WiFi是否連接
    fun isWifiConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val mWiFiNetworkInfo = mConnectivityManager
                .getNetworkInfo(ConnectivityManager.TYPE_WIFI)
            if (mWiFiNetworkInfo != null) {
                return mWiFiNetworkInfo.isAvailable
            }
        }
        return false
    }

    //判斷移動(dòng)網(wǎng)絡(luò)是否連接
    fun isMobileConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val mMobileNetworkInfo = mConnectivityManager
                .getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
            if (mMobileNetworkInfo != null) {
                return mMobileNetworkInfo.isAvailable
            }
        }
        return false
    }

    //獲取連接網(wǎng)絡(luò)的網(wǎng)絡(luò)信息
    fun getConnectedType(context: Context?): Int {
        if (context != null) {
            val mConnectivityManager = context
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val mNetworkInfo = mConnectivityManager.activeNetworkInfo
            if (mNetworkInfo != null && mNetworkInfo.isAvailable) {
                return mNetworkInfo.type
            }
        }
        return -1
    }

但是在完成代碼之后卻發(fā)現(xiàn)有些方法已經(jīng)被標(biāo)記為deprecated了翔悠,如isAvailable()getActiveNetworkInfo()方法业崖。查看代碼注釋野芒,其中提示我們應(yīng)該使用ConnectivityManager.NetworkCallback去監(jiān)聽(tīng)網(wǎng)絡(luò)連接變化,并使用registerNetworkCallback去注冊(cè)監(jiān)聽(tīng)双炕,內(nèi)容如下:

     * @deprecated Apps should instead use the
     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
     *             learn about connectivity changes.
     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
     *             {@link ConnectivityManager#registerNetworkCallback}. These will
     *             give a more accurate picture of the connectivity state of
     *             the device and let apps react more easily and quickly to changes.

接下來(lái)狞悲,我們對(duì)此網(wǎng)絡(luò)監(jiān)聽(tīng)api進(jìn)行簡(jiǎn)單說(shuō)明。

二妇斤、網(wǎng)絡(luò)監(jiān)聽(tīng)

ConnectivityManager.NetworkCallback是一個(gè)抽象類摇锋,具有如下幾個(gè)方法:

方法 描述
onAvailable(network: Network) 網(wǎng)絡(luò)連接成功回調(diào)
onUnavailable() 網(wǎng)絡(luò)連接超時(shí)或網(wǎng)絡(luò)不可達(dá)
onLost(Network network) 網(wǎng)絡(luò)已斷開(kāi)連接
onLosing(Network network, int maxMsToLive) 網(wǎng)絡(luò)正在丟失連接
onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) 網(wǎng)絡(luò)狀態(tài)變化
onLinkPropertiesChanged(Network network, LinkProperties linkProperties) 網(wǎng)絡(luò)連接屬性變化
onBlockedStatusChanged(Network network, boolean blocked) 訪問(wèn)的網(wǎng)絡(luò)阻塞狀態(tài)發(fā)生變化

其中最為常用的方法為onAvailable,onLost,onCapabilitiesChanged方法。

我們定義一個(gè)繼承自ConnectivityManager.NetworkCallback的子類:NetUtil站超,示例代碼如下:

package com.example.demowork1.util

import android.content.Context
import android.net.ConnectivityManager
import android.net.LinkProperties
import android.net.Network
import android.net.NetworkCapabilities


class NetUtil: ConnectivityManager.NetworkCallback() {

    //網(wǎng)絡(luò)連接成功
    override fun onAvailable(network: Network) {
        LogUtil.instance.d("網(wǎng)絡(luò)連接成功")
        super.onAvailable(network)
    }

    //網(wǎng)絡(luò)已斷開(kāi)連接
    override fun onLost(network: Network) {
        LogUtil.instance.d("網(wǎng)絡(luò)已斷開(kāi)連接")
        super.onLost(network)
    }

    override fun onLosing(network: Network, maxMsToLive: Int) {
        LogUtil.instance.d("網(wǎng)絡(luò)正在斷開(kāi)連接")
        super.onLosing(network, maxMsToLive)
    }

    //無(wú)網(wǎng)絡(luò)
    override fun onUnavailable() {
        LogUtil.instance.d("網(wǎng)絡(luò)連接超時(shí)或者網(wǎng)絡(luò)連接不可達(dá)")
        super.onUnavailable()
    }

    //當(dāng)網(wǎng)絡(luò)狀態(tài)修改(網(wǎng)絡(luò)依然可用)時(shí)調(diào)用
    override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
        super.onCapabilitiesChanged(network, networkCapabilities)
        LogUtil.instance.d( "net status change! 網(wǎng)絡(luò)連接改變")
    }

    //當(dāng)訪問(wèn)的網(wǎng)絡(luò)被阻塞或者解除阻塞時(shí)調(diào)用
    override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
        super.onBlockedStatusChanged(network, blocked)
    }

    //當(dāng)網(wǎng)絡(luò)連接屬性發(fā)生變化時(shí)調(diào)用
    override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
        super.onLinkPropertiesChanged(network, linkProperties)
    }
    ...
}

onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities)是我們較為常用的網(wǎng)絡(luò)狀態(tài)發(fā)生變化時(shí)的監(jiān)聽(tīng)荸恕,其中的參數(shù)networkCapabilities包含有一些我們常用的api ,其中保羅有兩個(gè)重要方法死相,如下:

  1. hasCapability
  2. hasTransport

hasCapability可以判斷網(wǎng)絡(luò)是否連接融求,有多個(gè)參數(shù)(參數(shù)可以查看接口NetCapability),其中常用參數(shù)如下:

參數(shù) 說(shuō)明
NetworkCapabilities.NET_CAPABILITY_INTERNET 表示是否連接上了互聯(lián)網(wǎng)(不關(guān)心是否可以上網(wǎng))
NetworkCapabilities.NET_CAPABILITY_VALIDATED 表示能夠和互聯(lián)網(wǎng)通信(這個(gè)為true表示能夠上網(wǎng))

hasTransport可以判斷網(wǎng)絡(luò)的類型媳纬,同樣有多個(gè)參數(shù)(參數(shù)可以查看接口Transport)双肤,其中常用的參數(shù)如下:

參數(shù) 說(shuō)明
TRANSPORT_WIFI 表示接入的是WIFI網(wǎng)絡(luò)
TRANSPORT_CELLULAR 表示接入的是數(shù)據(jù)網(wǎng)絡(luò)
TRANSPORT_BLUETOOTH 表示接入的是藍(lán)牙

具體的調(diào)用示例如下:

    //當(dāng)網(wǎng)絡(luò)狀態(tài)修改(網(wǎng)絡(luò)依然可用)時(shí)調(diào)用
    override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
        super.onCapabilitiesChanged(network, networkCapabilities)
        LogUtil.instance.d( "net status change! 網(wǎng)絡(luò)連接改變")
        // 表明此網(wǎng)絡(luò)連接成功驗(yàn)證
        if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
            if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                // 使用WI-FI
                LogUtil.instance.d("當(dāng)前在使用WiFi上網(wǎng)")
            } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                // 使用數(shù)據(jù)網(wǎng)絡(luò)
                LogUtil.instance.d("當(dāng)前在使用數(shù)據(jù)網(wǎng)絡(luò)上網(wǎng)")
            } else{
                LogUtil.instance.d("當(dāng)前在使用其他網(wǎng)絡(luò)")
                // 未知網(wǎng)絡(luò),包括藍(lán)牙钮惠、VPN等
            }
        }
    }
監(jiān)聽(tīng)調(diào)用

具體的調(diào)用實(shí)現(xiàn)如下:

    private fun setNetListener() {
        var request = NetworkRequest.Builder().build()
        var connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        connMgr.registerNetworkCallback(request, NetUtil.instance)
    }

三茅糜、 同步網(wǎng)絡(luò)狀態(tài)獲取

需要注意的是,雖然Google提供了此Callback方法來(lái)獲取網(wǎng)絡(luò)狀態(tài)的素挽,但是此回調(diào)是異步的蔑赘,如果需要同步獲取網(wǎng)絡(luò)狀態(tài),需要調(diào)用如下方法getNetworkCapabilities(Network network)方法來(lái)獲取當(dāng)前NetWork的連接狀態(tài)预明,示例代碼如下:

                val network = mConnectivityManager.activeNetwork ?: return false
                val status = mConnectivityManager.getNetworkCapabilities(network)
                        ?: return false
                if (status.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                    return true
                }

但是此方法是出現(xiàn)在Android M之后的SDK中缩赛,所以同步獲取網(wǎng)絡(luò)連接狀態(tài)的代碼還需要對(duì)Android的版本進(jìn)行判斷,最終代碼如下:

    /**
     * 判斷網(wǎng)絡(luò)是否連接
     */
    fun isNetworkConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                    .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                val mNetworkInfo = mConnectivityManager.activeNetworkInfo
                if (mNetworkInfo != null) {
                    return mNetworkInfo.isAvailable
                }
            } else {
                val network = mConnectivityManager.activeNetwork ?: return false
                val status = mConnectivityManager.getNetworkCapabilities(network)
                        ?: return false
                if (status.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                    return true
                }
            }
        }
        return false
    }

    /**
     * 判斷是否是WiFi連接
     */
    fun isWifiConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                    .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                val mWiFiNetworkInfo = mConnectivityManager
                        .getNetworkInfo(ConnectivityManager.TYPE_WIFI)
                if (mWiFiNetworkInfo != null) {
                    return mWiFiNetworkInfo.isAvailable
                }
            } else {
                val network = mConnectivityManager.activeNetwork ?: return false
                val status = mConnectivityManager.getNetworkCapabilities(network)
                        ?: return false
                if (status.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                    return true
                }
            }
        }
        return false
    }

    /**
     * 判斷是否是數(shù)據(jù)網(wǎng)絡(luò)連接
     */
    fun isMobileConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                    .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                val mMobileNetworkInfo = mConnectivityManager
                        .getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
                if (mMobileNetworkInfo != null) {
                    return mMobileNetworkInfo.isAvailable
                }
            } else {
                val network = mConnectivityManager.activeNetwork ?: return false
                val status = mConnectivityManager.getNetworkCapabilities(network)
                        ?: return false
                status.transportInfo
                if (status.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                    return true
                }
            }
        }
        return false
    }

總結(jié)

本文主要是對(duì)于獲取網(wǎng)絡(luò)狀態(tài)以及網(wǎng)絡(luò)監(jiān)聽(tīng)進(jìn)行說(shuō)明撰糠,需要注意的是對(duì)于同步和異步的區(qū)分酥馍。

參考文章

google官方文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市阅酪,隨后出現(xiàn)的幾起案子旨袒,更是在濱河造成了極大的恐慌,老刑警劉巖术辐,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砚尽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡辉词,警方通過(guò)查閱死者的電腦和手機(jī)必孤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)瑞躺,“玉大人敷搪,你說(shuō)我怎么就攤上這事兴想。” “怎么了购啄?”我有些...
    開(kāi)封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵襟企,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我狮含,道長(zhǎng)顽悼,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任几迄,我火速辦了婚禮蔚龙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘映胁。我一直安慰自己木羹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布解孙。 她就那樣靜靜地躺著坑填,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弛姜。 梳的紋絲不亂的頭發(fā)上脐瑰,一...
    開(kāi)封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音廷臼,去河邊找鬼苍在。 笑死,一個(gè)胖子當(dāng)著我的面吹牛荠商,可吹牛的內(nèi)容都是我干的寂恬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼莱没,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼初肉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起饰躲,我...
    開(kāi)封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤朴译,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后属铁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躬翁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年焦蘑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盒发。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡例嘱,死狀恐怖狡逢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拼卵,我是刑警寧澤奢浑,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站腋腮,受9級(jí)特大地震影響雀彼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜即寡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一徊哑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聪富,春花似錦莺丑、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至奸披,卻和暖如春昏名,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背源内。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工葡粒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膜钓。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓嗽交,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親颂斜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夫壁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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