转载请说明出处,本文来自Android菜鸟:Android中关于网络的一些知识点 QQ:2717521606
做Android开发的,免不了要使用到网络,这就经常需要用到一些网络相关的知识点,以前也没注意统计保存,时间久了有些知识就会忘,最近项目需要用到这些知识点,又花了一些时间学习,所以是时候记录一下以保存自己的学习成果了,免得每次用到都学习一次。
需要权限:
<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了。
需要权限:
<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之后,想要获取该网络的相关状态信息,还需配合调用ConnectivityManager的其他方法来获取,如下:
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了,在小米手机上,开启WPN之后,NetworkCapabilities.NET_CAPABILITY_INTERNET
和hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
都返回true
,而在公司手机上只有后者才返回true
,而且网络是正的OK的,可以连接联网的,NetworkCapabilities.NET_CAPABILITY_INTERNET
竟然返回了false,之前的代码又得推翻重写,哎,Android提供的这些SDK太不可靠了。可以NET_CAPABILITY_INTERNET
和NET_CAPABILITY_VALIDATED
一起判断,两者只要其中之一返回true结果就为true,当然这也需要大量测试这是否可靠,或者简单处理:判断activeNetwork或activeNetworkInfo是否为null就行了。 还有另一种方案,只使用旧版本的activeNetworkInfo.isConnected()做判断,经测试,这个方法在启动WPN后也返回true。
在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
// 判断是否是WPN网络
nc?.hasTransport(NetworkCapabilities.TRANSPORT_WPN) ?: false
注:判断网络类型时,只会有其中一个返回true,比如手机蜂窝网是OK的,此时连上wifi,则网络属于wifi类型。再比如,手机蜂窝网是OK的,开启WPN,则网络属于WPN类型。如果使用过时的NetworkInfo对象,开启WPN之后获取的网络类型是MOBILE,并不是WPN。使用新的方式时,如果开启了WPN那如何知道是蜂窝数据还是wifi数据?目前项目还没有需求知道这个,所以等以后有时间了再去了解。
(后续:之前是在Android7.1.1版本中测试是这样的,在Android10中测试的结盟,如果开了WPN,则网络类型为WIFI + WPN或者MOBILE + WPN)
还有下面的方法也能获取到网络信息,具体是哪些信息目前还没有去了解:
ConnectivityManager.getLinkProperties(network: Network?)
getNetworkPreference() 检索当前的首选网络类型。
setNetworkPreference(preference: Int) 指定首选的网络类型
ConnectivityManager.getLinkProperties(network: Network?)
使用小米手机实验,手机蜂窝数据是OK的,连接上一个没插网线的wifi之后,浏览器就访问不了网络了,是否可以设置wifi连接失败继续使用蜂窝数据继续请求网络呢?ConnectivityManager有如下一些方法:
isActiveNetworkMetered() 返回是否计量当前活动的数据网络。
isDefaultNetworkActive() 返回数据网络当前是否处于活动状态。
addDefaultNetworkActiveListener(l: ConnectivityManager.OnNetworkActiveListener!) 当系统的默认数据网络处于活动状态时,开始收听报告,这是执行网络流量的好时机。
关于不同版本之间ConnectivityManager.NetworkCallback的回调信息,目前只做了两个Android版本的测试:
Android7.1.1:WIFI + WPN(手机卡也可通WPN),把WIFI关掉,依次回调onLost、onAvailable、onCapabilitiesChanged。(走onLost的时候WPN已经断开,虽然手机状态栏上的WPN图标还在,而且调用WPN SDK的的方法判断WPN也是开着的,实际上WPN已经失效,已经访问不到WPN对应的资源了,这时候WPN SDK会尝试重连,此时肯定连接不上。注:不同的WPN可能处理不一样,应该等到onAvailable再重连)
Android10同样的测试:只回调onCapabilitiesChanged,会回调多次,刚开始是WPN,后来是CELLULAR + WPN(虽然看状态栏WPN图标还在,且WPNSDK方法获取WPN也是开着的,但此时WPN已经失效,可以看到WPNSDK有重连的操作,可以重连上,重连上之后的WPN才是有效的)
在android10上,如果手机不能可通WPN,从WIFI WPN网络关掉WIFI后,也只回调onCapabilitiesChanged,看到网络是CELLULAR|WPN,但是其实WPN已失效,然后可以看到WPNSDK在重连,当连接失败后就会彻底断开WPN,此时就会回调onLost、onAvailable、onCapabilitiesChanged。WPN重连时间超时是多久取决于WPNSDK,所以从WIFI WPN切换到一个不通WPN的网络时,不会立马看到onLost执行,而是要等到WPN彻底断开时才会执行。
Android7.1.1:CELLULAR + WPN(WIFI也可通WPN),连接WIFI,依次回调onAvailable、onCapabilitiesChanged。(走onAvailable的时候WPN已经断开,虽然状态栏上WPN的图标还在,这时候WPN SDK会尝试重连,可以重连上。注:不同的WPNSDK可能处理不一样)
Android10同样的测试:只回调onCapabilitiesChanged,可看到信息是WIFI+ WPN(不知道此时WPN是否还有效,但是看到WPNSDK有重连的操作,可以重连上)
切换网络后WPN会立即断开,虽然手机状态栏上的WPN图标还在,而且调用WPN SDK的的方法判断WPN也是开着的,实际上WPN已经失效,已经访问不到WPN对应的资源了。这种情况WPNSDK一般会尝试重新连接,能不能连上就看切换之后的网络是否可以通WPN服务器了。
Android7.1.1启动WPN成功之后 不会有任何回调。
Android10启动WPN成功后回调onAvailable、onCapabilitiesChanged,可以看到网络信息为WIFI + WPN或CELLULAR + WPN
使用WPNSDK调用方法断开WPN,或者SDK检测网络不可用断开WPN,此时手机状态栏上没有WPN图标了。
不论是Android7.1.1还是Android10,断开WPN之后都会回调onAvailable、onCapabilitiesChanged