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!) 当系统的默认数据网络处于活动状态时,开始收听报告,这是执行网络流量的好时机。

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