Android中關於網絡的一些知識點

前言

轉載請說明出處,本文來自Android菜鳥:https://blog.csdn.net/android_cai_niao/article/details/106329664 QQ:2717521606

做Android開發的,免不了要使用到網絡,這就經常需要用到一些網絡相關的知識點,以前也沒注意統計保存,時間久了有些知識就會忘,最近項目需要用到這些知識點,又花了一些時間學習,所以是時候記錄一下以保存自己的學習成果了,免得每次用到都學習一次。

1、關於獲取本機ip地址

需要權限:

<uses-permission android:name="android.permission.INTERNET"/>
/** 獲取本機ip地址列表,Wifi、電話卡和網線的ip都能獲取到(手機也能插網線的) */
@Suppress("MemberVisibilityCanBePrivate")
fun getLocalIpList() = NetworkInterface.getNetworkInterfaces().asSequence()
	// 過濾:已啓動並且正在運行的接口,且要有ip地址,且不能是環回接口(即把數據發送給本機的網絡接口,ipv4爲127.0.0.1,ipv6爲::1)
	.filter { ni: NetworkInterface -> ni.isUp && ni.inetAddresses.toList().isNotEmpty() && !ni.isLoopback }
	// 把每個網絡接口中的ip地址列表合成一個大列表
	.flatMap { ni: NetworkInterface -> ni.inetAddresses.asSequence() }
	// 過濾:如果是ipv6,不能是連接本地地址
	.filter { ip: InetAddress -> !(ip is Inet6Address && ip.isLinkLocalAddress) }
	.toList()

在小米手機下測試:插有sim卡,同時連接上wifi,發現sim卡和wifi的ip獲取到了,所以在獲取本機ip時應該判斷一下網絡請求使用的是哪個ip,一般情況下,連接上wifi之後 ,wifi會變成默認網絡(使用wifi來傳輸數據),所以在ip有多個時,可以優先取wifi的ip:

wifi網絡接口名稱:wlan0

移動網絡接口名稱:rmnet_data0,也不百分百是這個,比如公司的手機,移動APN卡爲:ccmni0

如果沒有wifi,但是ip還是有多個,怎麼取?這種情況可以讓後臺做一個接口,讓他返回我們的ip,聽說我們在訪問後臺時,後臺是很容易拿到我們的ip的。

注:有的手機,連接上wifi之後,獲取ip時就只有wifi的ip了。

2、判斷網絡是否OK

需要權限:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
fun hasActiveNetwork(context: Context): Boolean {
val manager= context.connectivityManager
// 當關閉“數據連接”開關時,而且沒有Wifi,則activeNetwork或activeNetworkInfo爲null
// 當sim卡過期不管有無信號,activeNetwork或activeNetworkInfo爲null
// 只要有sim卡或者連接上wifi,不論是否能連接互聯網,都會返回true。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
	// Android 6.0或以上
	val activeNetwork = manager.activeNetwork
	if (activeNetwork != null) {
		val networkCapabilities = manager.getNetworkCapabilities(activeNetwork)
		return networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
	}
	return false
} else {
	// Android 6.0以下
	// 在啓動網絡流量之前,應始終檢查isConnected()。如果沒有默認網絡,則可能返回null。
	// 插着手機卡,連接上Wifi時,Wifi會變成活動網絡。
	val ani = manager.activeNetworkInfo
	return ani?.isConnected ?: false
}

在Android6.0或以上版本,調用ConnectivityManager的activeNetworkInfo會提示過時,使用activeNetwork替代。這兩個方法,一個是返回NetworkInfo對象,一個是返回Network對象,區別如下:

  • Network:表示一個網絡,通過這個對象,我們可以對網絡進行控制
  • NetworkInfo:表示一個網絡的狀態,可以獲取到一個網絡的相關狀態信息(比如網絡類型)。

所以得到Network之後,想要獲取該網絡的相關狀態信息,還需配合調用ConnectivityManager的其他方法來獲取,如下:

  • ConnectivityManager.getNetworkInfo(network: Network?) (過時)
  • ConnectivityManager.getNetworkCapabilities(network: Network?)
  • ConnectivityManager.getLinkProperties(network: Network?)

NetworkInfo已經過時,所以對應的getNetworkInfo(network: Network?)也是過時的。

上面方法只能判斷是否有可用的活動網絡,比如這個sim卡的數據開關是打開的,或者wifi是連上了的,但是能不能真正訪問到互聯網是不知道。在Android6.0或以上版本時,可以使用如下方法檢查是否有可以聯接互聯網的網絡:

networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)

經測試,在小米手機上是OK的,當連接上一個沒插網線的wifi時它返回false,連接一個有網的wifi時,它返回true。但是在我們公司的手機上,連接沒插網線的wifi時它返回true,所以說hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)是沒有保證的,正確的做法是使用·NetworkCapabilities.NET_CAPABILITY_INTERNET判斷是否有可用網絡,然後再連接公司服務器,能連接上了才認爲網絡是OK的。

後續,又出Bug了,在小米手機上,開啓VPN之後,NetworkCapabilities.NET_CAPABILITY_INTERNEThasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)都返回true,而在公司手機上只有後者才返回true,而且網絡是正的OK的,可以連接聯網的,NetworkCapabilities.NET_CAPABILITY_INTERNET竟然返回了false,之前的代碼又得推翻重寫,哎,Android提供的這些SDK太不可靠了。可以NET_CAPABILITY_INTERNETNET_CAPABILITY_VALIDATED一起判斷,兩者只要其中之一返回true結果就爲true,當然這也需要大量測試這是否可靠,或者簡單處理:判斷activeNetwork或activeNetworkInfo是否爲null就行了。 還有另一種方案,只使用舊版本的activeNetworkInfo.isConnected()做判斷,經測試,這個方法在啓動VPN後也返回true。

3、獲取網絡信息

在Android6.0以前,獲取網絡信息的方式如下:

// connectivityManager.allNetworkInfo 獲取所有網絡的網絡信息
val ani = connectivityManager.activeNetworkInfo // 只獲取活動網絡的網絡信息
ani.extraInfo 	// APN,如:3gwap或"Dazhou2105"(連Wifi時)。報告有關網絡狀態的額外信息(如果較低的網絡層提供了這些信息)。
ani.reason		//如:connected。報告嘗試建立連接失敗的原因(如果有)。
ani.state		// 如:CONNECTED。報告網絡的當前粗粒度狀態。
ani.subtypeName	// 如:LTE。返回描述網絡子類型的易於理解的名稱。
ani.type		// 網絡類型,int類型,對應ConnectivityManager中定義的常量
ani.typeName	// 如:MOBILE。返回一個易於理解的名稱,描述網絡的類型,例如“ WIFI”或“ MOBILE”。
ani.isAvailable
ani.isConnected // 連Wifi時,就算wifi路由沒插網線也會返回true。也就是說connected爲true不代碼可以訪問互聯網,要用ping的方式或者連接服務器的方式
ani.isConnectedOrConnecting

在Android6.0或以上版本,獲取網絡信息的方式如下:

// connectivityManager.allNetworks 獲取所有網絡
val network = connectivityManager.activeNetwork // 只獲取活動網絡的網絡
cm.getNetworkCapabilities(activeNetwork)// 獲取網絡信息對象
// 判斷是否有可用的網絡(不能確保網絡是否能連接互聯網)
nc?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
// 判斷是否有可以連接互聯網的網絡(有的手機使用此方法並不準確)
nc?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ?: false
// 判斷是否是wifi網絡
nc?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ?: false
// 判斷是否是蜂窩網絡
nc?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ?: false
// 判斷是否是VPN網絡
nc?.hasTransport(NetworkCapabilities.TRANSPORT_VPN) ?: false

注:判斷網絡類型時,只會有其中一個返回true,比如手機蜂窩網是OK的,此時連上wifi,則網絡屬於wifi類型。再比如,手機蜂窩網是OK的,開啓VPN,則網絡屬於VPN類型。如果使用過時的NetworkInfo對象,開啓VPN之後獲取的網絡類型是MOBILE,並不是VPN。使用新的方式時,如果開啓了VPN那如何知道是蜂窩數據還是wifi數據?目前項目還沒有需求知道這個,所以等以後有時間了再去了解。

還有下面的方法也能獲取到網絡信息,具體是哪些信息目前還沒有去了解:

ConnectivityManager.getLinkProperties(network: Network?)

4、監聽網絡狀態

5、其實一些方法,或者待學習的

  • getNetworkPreference() 檢索當前的首選網絡類型。

  • setNetworkPreference(preference: Int) 指定首選的網絡類型

  • ConnectivityManager.getLinkProperties(network: Network?)

  • 使用小米手機實驗,手機蜂窩數據是OK的,連接上一個沒插網線的wifi之後,瀏覽器就訪問不了網絡了,是否可以設置wifi連接失敗繼續使用蜂窩數據繼續請求網絡呢?ConnectivityManager有如下一些方法:

    • bindProcessToNetwork(network: Network?)
    • setProcessDefaultNetwork(network: Network?) 將當前進程綁定到network。
    • requestNetwork(request: NetworkRequest, networkCallback: onnectivityManager.NetworkCallback) 此方法似乎可以指定網絡如wifi不行時使用蜂窩網絡
  • isActiveNetworkMetered() 返回是否計量當前活動的數據網絡。

  • isDefaultNetworkActive() 返回數據網絡當前是否處於活動狀態。

  • addDefaultNetworkActiveListener(l: ConnectivityManager.OnNetworkActiveListener!) 當系統的默認數據網絡處於活動狀態時,開始收聽報告,這是執行網絡流量的好時機。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章