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