kotlin获取设备的公网IP

xzbxzb 安卓 2026-02-24 14 0
Android 里没有直接“读系统公网IP”的 API,只能通过请求外网接口拿到当前出口公网 IP。
下面 Kotlin 可用的最简方案。

一、最稳方案:请求公开 IP 接口

权限(AndroidManifest.xml)

<uses-permission android:name="android.permission.INTERNET" />

Kotlin 代码(协程版)

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.URL

/** 获取公网IP */
suspend fun getPublicIp(): String = withContext(Dispatchers.IO) {
    try {
        // 常用接口,任选一个
        val ip = URL("https://api.ipify.org").readText()
        // val ip = URL("https://icanhazip.com").readText()
        ip
    } catch (e: Exception) {
        e.toString()
    }
}
调用示例:
lifecycleScope.launch {
    val ip = getPublicIp()
    Log.d("PublicIP", ip)
}

遇到的 java.net.ConnectException 是典型的网络连接失败问题,api.ipify.org 虽然稳定,但会受网络环境(如代理、防火墙、DNS解析、HTTPS证书)影响导致连接失败。下面完整的解决方案,包含「备用接口 + 超时重试 + 异常处理」,彻底解决这个问题。

上面使用时返回java.net.ConnectException: Failed to connect to api.ipify.org/172.67.74.152:443错误,更改方案如下

第一步:先理解错误原因

Failed to connect to api.ipify.org/172.67.74.152:443 表示:
  1. DNS解析到了 172.67.74.152,但无法建立TCP连接(443端口是HTTPS);

  2. 常见原因:网络禁用HTTPS、代理配置错误、接口被墙、设备网络权限未生效。

第二步:修复后的完整代码(Android + Kotlin)

这个版本包含多接口重试 + 超时控制 + 详细异常提示,稳定性大幅提升:
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.HttpURLConnection
import java.net.URL
import java.util.concurrent.TimeUnit

object IpUtils {
    // 备用公网IP接口列表(优先选国内可访问的)
    private val IP_API_LIST = listOf(
        "https://api.ipify.org",          // 基础版
        "https://icanhazip.com",          // 备用1
        "https://ifconfig.me/ip",         // 备用2
        "http://icanhazip.com",           // 降级为HTTP(避免HTTPS证书问题)
        "http://ifconfig.me/ip"           // 降级为HTTP(最后兜底)
    )

    /**
     * 获取公网IP(带重试+超时)
     * @return 公网IP字符串,失败返回具体原因
     */
    suspend fun getPublicIpWithRetry(): String = withContext(Dispatchers.IO) {
        var lastException: Exception? = null

        // 遍历接口列表,逐个尝试
        for (apiUrl in IP_API_LIST) {
            try {
                val url = URL(apiUrl)
                // 手动创建连接,添加超时控制(关键)
                val connection = url.openConnection() as HttpURLConnection
                connection.apply {
                    connectTimeout = 5000  // 连接超时5秒
                    readTimeout = 5000     // 读取超时5秒
                    requestMethod = "GET"
                    instanceFollowRedirects = true  // 允许重定向
                }

                // 检查响应码
                if (connection.responseCode == 200) {
                    val ip = connection.inputStream.bufferedReader().readText().trim()
                    connection.disconnect()
                    // 过滤空值/无效IP
                    if (ip.isNotBlank() && ip.contains(".")) {
                        return@withContext ip
                    }
                }
            } catch (e: Exception) {
                lastException = e
                // 打印当前接口失败原因,继续试下一个
                println("接口 $apiUrl 失败: ${e.message}")
                continue
            }
        }

        // 所有接口都失败,返回最后一次异常信息
        return@withContext "获取失败: ${lastException?.message ?: "未知错误"}"
    }
}

第三步:调用方式

// 在Activity/Fragment中调用
lifecycleScope.launch {
    val ip = IpUtils.getPublicIpWithRetry()
    // 更新UI(已回到主线程)
    tvIp.text = "公网IP:$ip"
}

第四步:额外排查步骤(如果仍失败)

如果修复后还是连接失败,按以下顺序排查:
  1. 检查网络权限:确认 AndroidManifest.xml 中

    已添加,且没有

    缺失(可选);

  2. 关闭代理/VPN:如果设备开启了代理、VPN,先关闭再测试(很多时候是代理导致HTTPS连接失败);

  3. 测试HTTP接口:代码中已加入HTTP接口兜底,优先尝试 http://icanhazip.com(避免HTTPS证书问题);

  4. 检查网络环境:切换Wi-Fi/手机流量测试(部分局域网会屏蔽外网接口);

  5. 手动验证接口:在浏览器/Postman中访问 https://api.ipify.org,确认能返回IP(如果浏览器也打不开,说明该接口在当前网络不可用)。

总结

  1. 核心问题:api.ipify.org 连接失败是网络环境导致,而非代码问题;

  2. 解决方案:通过「多接口重试 + 超时控制 + HTTP降级」提升稳定性;

  3. 排查重点:优先检查网络权限、代理/防火墙、接口可访问性。


 您阅读本篇文章共花了: 

版权声明

本文章如果涉及侵权,请联系我。
部分文章系本人原创未经许可,不得转载。

喜欢0发布评论

评论列表

发表评论

  • 昵称(必填)
  • 邮箱
  • 网址