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

前言

在APP的開發(fā)中,獲取到網(wǎng)絡(luò)的鏈接狀態(tài)是一個(gè)經(jīng)常使用到的方法。除了可以使用ping指令來判斷當(dāng)前的網(wǎng)絡(luò)狀況之外,還可以直接通過ConnectivityManager來對(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)聽網(wǎng)絡(luò)連接變化,并使用registerNetworkCallback去注冊(cè)監(jiān)聽,內(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.

接下來,我們對(duì)此網(wǎng)絡(luò)監(jiān)聽api進(jìn)行簡(jiǎn)單說明。

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

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ò)已斷開連接
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ǎ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ò)已斷開連接
    override fun onLost(network: Network) {
        LogUtil.instance.d("網(wǎng)絡(luò)已斷開連接")
        super.onLost(network)
    }

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

    //無網(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ǎ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)聽,其中的參數(shù)networkCapabilities包含有一些我們常用的api ,其中保羅有兩個(gè)重要方法,如下:

  1. hasCapability
  2. hasTransport

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

參數(shù) 說明
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ù) 說明
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)聽調(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方法來獲取網(wǎng)絡(luò)狀態(tài)的,但是此回調(diào)是異步的,如果需要同步獲取網(wǎng)絡(luò)狀態(tài),需要調(diào)用如下方法getNetworkCapabilities(Network network)方法來獲取當(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)聽進(jìn)行說明,需要注意的是對(duì)于同步和異步的區(qū)分。

參考文章

google官方文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容