前言
在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è)重要方法死相,如下:
- hasCapability
- 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ū)分酥馍。