前言
因为项目中经常会遇到要上传一系列设备信息的功能,为了方便使用,所以就拆分成以下系列文章来单独介绍如何获取各类设备信息
1. 移动流量IP地址获取
通过NetworkInterface的getNetworkInterfaces() 方法和getInetAddresses() 方法可以得到该节点的所有IP地址
1.1 步骤
- 通过
NetworkInterface.getNetworkInterfaces() 获取该机器上关于NetworkInterface的枚举 - 遍历该枚举,通过
getInetAddress() 获取该节点的所有IP地址 - 从IP地址列表中获取是IPV4且不是回路地址的正确IP地址
1.2 具体实现
private fun getMobileIP(): String? {
try {
val networkInterfaceEnumeration = NetworkInterface.getNetworkInterfaces()
while (networkInterfaceEnumeration.hasMoreElements()) {
val networkInterface = networkInterfaceEnumeration.nextElement()
val inetAddressEnumeration = networkInterface.inetAddresses
while (inetAddressEnumeration.hasMoreElements()) {
val inetAddress = inetAddressEnumeration.nextElement()
if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) {
return inetAddress.getHostAddress()
}
}
}
} catch (e: SocketException) {
return null
e.printStackTrace()
}
return null
}
::1 IPv6形式,对应IPV4的127.0.0.1
127.0.0.1
fe80::c4eb:7d14:a746:a087%wlan0
192.168.20.20
1.3 注意
该方法仅适用于只开启了移动流量未开启WIFI的时候使用,因为我们在遍历并判断是IPV4的地址时,获取到了第一个就直接返回了,而且该方法获取到的也是内网A类地址
while (inetAddressEnumeration.hasMoreElements()) {
val inetAddress = inetAddressEnumeration.nextElement()
if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) {
return inetAddress.getHostAddress()
}
}
当同时连接WIFI和移动流量时,将其打印,可以得到以下地址
10.98.193.41
192.168.137.21
可以看到,其实是有两个IP的,一个是A类的内网地址,是移动流量的,另一个则是Wifi的C类内网地址。
如果我们想要移动流量WIFI同时连接时使用,那么就将上面的判断改一下,去除掉C类地址后,得到的就是移动运营商的内网IP。
2. Wifi下IP地址获取
2.1 内网IP地址获取
2.1.1 方法一
通过小结1中的方法获取
2.1.2 方法二
通过WifiManager来获取Wifi信息
2.1.2.1 所需权限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
2.1.2.2 步骤
- 通过WifiManager的
getConnectionInfo() 方法来获取WifiInfo信息 - 通过WifiInfo的
getIpAddress() 方法获取IP地址 - 将Int类型的IPv4转为String类型
2.1.2.3 具体实现
private fun getWifiIP(): Int {
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiInfo = wifiManager.connectionInfo
return wifiInfo.ipAddress
}
private fun intIP2StringIP(ip: Int): String? {
return (ip and 0xFF).toString() + "." +
(ip shr 8 and 0xFF) + "." +
(ip shr 16 and 0xFF) + "." +
(ip shr 24 and 0xFF)
}
-------------------------------------IP转Int数值------------------------------------------
192(10)=1100 0000(2)
168(10)=1010 1000(2)
1(10) =0000 0001(2)
100(10)=0110 0100(2)
连在一起就是
11000000101010000000000101100100
对应的int数字是-1062731420
192左移24位 11000000 00000000 00000000 00000000
168左移16位 00000000 10101000 00000000 00000000
1 左移8位 00000000 00000000 00000001 00000000
100左移0位 00000000 00000000 00000000 01100100
-----------------------------------------Int转IP------------------------------------------
1100 0000 1010 1000 0000 0001 0110 0100 (-1062731420)
右移24位(-64) and 1111 1111 后为:
1111 1111 1111 1111 1111 1111 1100 0000 192
右移16位(-16216)
1111 1111 1111 1111 1100 0000 1010 1000 168
右移8位(-4151295)
1111 1111 1100 0000 1010 1000 0000 0001 1
右移0位(-1062731420)
1100 0000 1010 1000 0000 0001 0110 0100 100
位运算,只能是Int和Long类型,
shl 左移位<<
shr 右移位>>(用符号位的值来填充) 1是负数0是正数
2.2 外网IP地址获取
2.2.1 思路
在本地,通过WifiManager获取到是内网IP,无法获取到外网IP,需要借助服务器,所以我们通过网络请求来获取
这里我们通过Get请求访问http://pv.sohu.com/cityjson 搜狐的地址,即可得到以下内容
var returnCitySN = {
"cip": "202.97.159.66",
"cid": "140100",
"cname": "山西省太原市"
};
对该字符串进行解析即可得到本机的外网IP
val start: Int = msg.indexOf("{")
val end: Int = msg.indexOf("}")
val ip = JSONObject(msg.substring(start, end + 1)).get("cip") as String
在具体项目中,可以让后端出个接口来返回我们的外网IP,毕竟别人的接口什么时候不通了也说不准。
2.2.2 具体实现
interface IPService {
@GET
fun getIP(@Url url: String): Call<ResponseBody>
}
val retrofit= Retrofit.Builder()
.baseUrl("http://a")
.build()
val api =retrofit.create(IPService::class.java)
api.getIP("http://pv.sohu.com/cityjson").enqueue(object :Callback<ResponseBody>{
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
response.body()?.string()?.let {
val start: Int = it.indexOf("{")
val end: Int = it.indexOf("}")
val ip = JSONObject(it.substring(start, end + 1)).get("cip") as String
Log.i(TAG, "网络请求下的IP为:$ip")
}
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
Toast.makeText(this@IPActivity,"获取IP失败:${t.message}",Toast.LENGTH_SHORT).show()
}
})
3. 判断网络类型
判断是移动网络还是Wifi,这里我们用到了ConnectivityManager类,该类可以监测网络的连接
3.1所需权限
ACCESS_NETWORK_STATE
3.2 方法
getActiveNetwork() 只能Android6以上才能调用,所以Android 6以下,我们使用getActiveNetworkInfo() 方法来获取。
private fun judgeNetType():Int{
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M){
val mNetworkInfo = connectivityManager.activeNetworkInfo
return if (mNetworkInfo != null) {
when (mNetworkInfo.type) {
ConnectivityManager.TYPE_MOBILE -> {
1
}
ConnectivityManager.TYPE_WIFI -> {
2
}
else -> {
0
}
}
}else{
3
}
}else{
val network = connectivityManager.activeNetwork
if (network != null) {
val nc = connectivityManager.getNetworkCapabilities(network)
return if (nc != null){
when {
nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> {
1
}
nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> {
2
}
else -> {
0
}
}
}else{
3
}
}else{
return 3
}
}
}
4. 总结
获取IP的方式无非就是以下3种
-
NetworkInterface.getNetworkInterfaces() +getInetAddress() 可以得到该节点的所有IP地址,筛选是IPv4的即我们所要的IP,得到的都是内网IP -
通过WifiManager的getConnectionInfo() +getIpAddress() 即可得到WIFI下的IP地址,同样,得到的是内网IP -
通过访问外部服务器,由服务器通过接口告诉我们自己的外网IP地址
所以我们在项目中使用时,如果想要获取外网IP,只能通过访问外部服务器;如果想要获取WIFI的内网IP,第一二种方式均行;想要获取移动网络的的内网IP,用第一种方式,并筛选是10段的内网IP(不同地方的运营商可能会有区别)。
项目地址
如果本文对你有帮助,请别忘记点赞start,如果有不恰当的地方也请提出来,下篇文章见。
5. 参考文章
Java实现ip地址和int数字的相互转换
|