如何从代码中获取设备的IP地址?

459

有没有可能使用一些代码获取设备的IP地址?


6
请记住,这是一个大小为N的集合,您不能假设N ==(0 || 1)。换句话说,不要假设设备只有一种与网络通信的方式,也不要假定它根本没有与网络通信的方式。 - James Moore
相关:https://dev59.com/IGkx5IYBdhLWcg3wCP-Y - AlikElzin-kilaka
2
如何查看我的Android手机的IP地址? - Ciro Santilli OurBigBook.com
你应该从外部服务获取它,http://ipof.in/txt 就是这样的一个服务。 - vivekv
在Android中,是否可以通过编程方式获取它? - Tanmay Sahoo
请查看我的答案:https://dev59.com/ymQn5IYBdhLWcg3wkH3U#50871614 - Yong
32个回答

486

这是我的辅助工具,用于读取IP和MAC地址。实现纯Java,但在getMACAddress()中有一个注释块,可以从特殊的Linux(Android)文件中读取值。我只在少数设备和模拟器上运行了此代码,如果您发现奇怪的结果,请在此向我提出。

// AndroidManifest.xml permissions
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

// test functions
Utils.getMACAddress("wlan0");
Utils.getMACAddress("eth0");
Utils.getIPAddress(true); // IPv4
Utils.getIPAddress(false); // IPv6 

Utils.java

import java.io.*;
import java.net.*;
import java.util.*;   
//import org.apache.http.conn.util.InetAddressUtils;

public class Utils {

    /**
     * Convert byte array to hex string
     * @param bytes toConvert
     * @return hexValue
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuilder sbuf = new StringBuilder();
        for(int idx=0; idx < bytes.length; idx++) {
            int intVal = bytes[idx] & 0xff;
            if (intVal < 0x10) sbuf.append("0");
            sbuf.append(Integer.toHexString(intVal).toUpperCase());
        }
        return sbuf.toString();
    }

    /**
     * Get utf8 byte array.
     * @param str which to be converted
     * @return  array of NULL if error was found
     */
    public static byte[] getUTF8Bytes(String str) {
        try { return str.getBytes("UTF-8"); } catch (Exception ex) { return null; }
    }

    /**
     * Load UTF8withBOM or any ansi text file.
     * @param filename which to be converted to string
     * @return String value of File
     * @throws java.io.IOException if error occurs
     */
    public static String loadFileAsString(String filename) throws java.io.IOException {
        final int BUFLEN=1024;
        BufferedInputStream is = new BufferedInputStream(new FileInputStream(filename), BUFLEN);
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream(BUFLEN);
            byte[] bytes = new byte[BUFLEN];
            boolean isUTF8=false;
            int read,count=0;           
            while((read=is.read(bytes)) != -1) {
                if (count==0 && bytes[0]==(byte)0xEF && bytes[1]==(byte)0xBB && bytes[2]==(byte)0xBF ) {
                    isUTF8=true;
                    baos.write(bytes, 3, read-3); // drop UTF8 bom marker
                } else {
                    baos.write(bytes, 0, read);
                }
                count+=read;
            }
            return isUTF8 ? new String(baos.toByteArray(), "UTF-8") : new String(baos.toByteArray());
        } finally {
            try{ is.close(); } catch(Exception ignored){} 
        }
    }

    /**
     * Returns MAC address of the given interface name.
     * @param interfaceName eth0, wlan0 or NULL=use first interface 
     * @return  mac address or empty string
     */
    public static String getMACAddress(String interfaceName) {
        try {
            List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface intf : interfaces) {
                if (interfaceName != null) {
                    if (!intf.getName().equalsIgnoreCase(interfaceName)) continue;
                }
                byte[] mac = intf.getHardwareAddress();
                if (mac==null) return "";
                StringBuilder buf = new StringBuilder();
                for (byte aMac : mac) buf.append(String.format("%02X:",aMac));  
                if (buf.length()>0) buf.deleteCharAt(buf.length()-1);
                return buf.toString();
            }
        } catch (Exception ignored) { } // for now eat exceptions
        return "";
        /*try {
            // this is so Linux hack
            return loadFileAsString("/sys/class/net/" +interfaceName + "/address").toUpperCase().trim();
        } catch (IOException ex) {
            return null;
        }*/
    }

    /**
     * Get IP address from first non-localhost interface
     * @param useIPv4   true=return ipv4, false=return ipv6
     * @return  address or empty string
     */
    public static String getIPAddress(boolean useIPv4) {
        try {
            List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface intf : interfaces) {
                List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
                for (InetAddress addr : addrs) {
                    if (!addr.isLoopbackAddress()) {
                        String sAddr = addr.getHostAddress();
                        //boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr);
                        boolean isIPv4 = sAddr.indexOf(':')<0;

                        if (useIPv4) {
                            if (isIPv4) 
                                return sAddr;
                        } else {
                            if (!isIPv4) {
                                int delim = sAddr.indexOf('%'); // drop ip6 zone suffix
                                return delim<0 ? sAddr.toUpperCase() : sAddr.substring(0, delim).toUpperCase();
                            }
                        }
                    }
                }
            }
        } catch (Exception ignored) { } // for now eat exceptions
        return "";
    }

}

声明: 这个Utils类的想法和示例代码来自于多篇SO文章和Google搜索。我已经清理并合并了所有示例。


19
因为涉及到getHardwareAddress()方法,所以需要API 9及以上版本。 - Calvin
2
问题 - toUpperCase() 上的 lint 警告。捕获 Exception 总是很棘手的(并且帮助方法应该抛出异常,让调用者处理异常 - 尽管没有修改这一点)。格式化:不应超过 80 行。getHardwareAddress() 的条件执行 - 补丁:https://github.com/Utumno/AndroidHelpers/commit/b285df75ab6dd1e724e1985d480d18be3a8692a7。你怎么看? - Mr_and_Mrs_D
6
如果你连接到本地网络(例如WiFi或模拟器),你会得到一个私有IP地址。你可以通过向特定网站发出请求来获取代理IP地址,该网站会提供给你代理地址,例如http://whatismyip.akamai.com/。 - Julien Kronegg
1
这对我来说在使用Wifi的真实设备上完美运行。非常感谢,兄弟。 - Neo
8
当我尝试获取IP地址时,我的Nexus 6出现了糟糕的结果。我有一个名为“name:dummy0(dummy0)”的NetworkInterface,它提供了一个格式为“/XX::XXXX:XXXX:XXXX:XXXX%dummy0”的地址,还有一个对应于wlan0的真实网络接口,但由于“dummy”首先出现,所以我总是得到那个虚拟地址。 - Julian Suarez
显示剩余29条评论

238

AndroidManifest.xml 中声明了 ACCESS_WIFI_STATE 权限:

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

可以使用WifiManager获取IP地址:

Context context = requireContext().getApplicationContext();
WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
String ip = Formatter.formatIpAddress(wm.getConnectionInfo().getIpAddress());

10
这个对我有用。但是,它需要“ACCESS_WIFI_STATE”权限,正如“Umair”所写,列表用法是不必要的。 - android developer
16
formatIpAddress因某些原因已被弃用。应该使用什么替代品? - android developer
8
从文档中得知:使用 getHostAddress() 方法,该方法支持 IPv4 和 IPv6 地址。原文中的语句似乎是错误的,这个方法实际上不支持 IPv6 地址。请注意,本次翻译已经将原文的错误进行了修正。 - Ryan R
7
如何在获取服务器和客户端IP地址时使用getHostAddress()函数,@RyanR? - gumuruh
53
用户使用数据而不是WiFi,这仍然有效吗? - PinoyCoder
显示剩余12条评论

90
public static String getLocalIpAddress() {
    try {
        for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
            NetworkInterface intf = en.nextElement();
            for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                InetAddress inetAddress = enumIpAddr.nextElement();
                if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
                    return inetAddress.getHostAddress();
                }
            }
        }
    } catch (SocketException ex) {
        ex.printStackTrace();
    }
    return null;
}

我已经添加了 inetAddress instanceof Inet4Address 来检查它是否为IPv4地址。


1
除了访问一些外部 REST 端点以获取公共 IP 地址之外,没有其他方法可以获得公共 IP 地址。例如:https://api.ipify.org/?format=json。设备甚至不知道自己的公共 IP 地址。 - Jeffrey Blattman

67

我使用了以下代码:

//获取IP地址
String ipAddress = InetAddress.getLocalHost().getHostAddress();
//使用hashCode将IP地址转换为int
int ipInt = ipAddress.hashCode();
//将int转换回正确格式的IP地址
String formattedIpAddress = String.format("%d.%d.%d.%d",
        (ipInt & 0xff), (ipInt >> 8 & 0xff),
        (ipInt >> 16 & 0xff), (ipInt >> 24 & 0xff));

我选择使用hashCode的原因是,当我使用getHostAddress时,IP地址后面会附加一些垃圾值。但是hashCode对我非常有效,因为然后我可以使用Formatter来以正确的格式获取IP地址。

这里是示例输出:

1.使用getHostAddress***** IP=fe80::65ca:a13d:ea5a:233d%rmnet_sdio0

2.使用hashCodeFormatter***** IP=238.194.77.212

正如您所看到的,第二种方法给我提供了我所需要的精确结果。

public String getLocalIpAddress() {
    try {
        for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
            NetworkInterface intf = en.nextElement();
            for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                InetAddress inetAddress = enumIpAddr.nextElement();
                if (!inetAddress.isLoopbackAddress()) {
                    String ip = Formatter.formatIpAddress(inetAddress.hashCode());
                    Log.i(TAG, "***** IP="+ ip);
                    return ip;
                }
            }
        }
    } catch (SocketException ex) {
        Log.e(TAG, ex.toString());
    }
    return null;
}

1
getHostAddress()将执行您添加的格式化程序内容相同的操作。 - Phil
12
使用hashCode是完全错误的,会返回无意义的结果。应该使用InetAddress.getHostAddress()方法。 - Pointer Null
如果把这段代码改为以下内容:if (!inetAddress.isLoopbackAddress() && InetAddressUtils.isIPv4Address(inetAddress.getHostAddress())) { return inetAddress.getHostAddress().toString(); }将会给你正确的IP格式。 - Chuy47
代码仅返回第一个IP地址,手机可能同时具有蜂窝网络、WIFI和蓝牙地址。 - reker
@Chuy47,它说找不到InetAddressUtils。 - FabioR
显示剩余4条评论

62

虽然有一个正确答案,但我在此分享我的答案,希望这种方式更加方便。

WifiManager wifiMan = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInf = wifiMan.getConnectionInfo();
int ipAddress = wifiInf.getIpAddress();
String ip = String.format("%d.%d.%d.%d", (ipAddress & 0xff),(ipAddress >> 8 & 0xff),(ipAddress >> 16 & 0xff),(ipAddress >> 24 & 0xff));

6
谢谢!Formatter已被弃用,我真的不想写简单的位逻辑。 - William Morrison
7
功能很好,但需要WIFI_STATE权限: <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> - Brent Faust
1
我使用了格式化工具,但它并没有起作用。非常好!真的很感激。您能解释一下最后一行做了什么吗?我知道 %d.%d.%d.%d ,但其他的呢?谢谢。 - Günay Gültekin
1
不,这并没有直接回答 OP 的问题。因为并非所有的 Android 设备都使用 WiFi 连接到互联网。它可能会在以太网上使用 NATed LAN,或者使用 BT 而不是 NATed WAN 连接等。 - nyconing

34

以下代码可能会对你有帮助... 不要忘记添加权限...

public String getLocalIpAddress(){
   try {
       for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();  
       en.hasMoreElements();) {
       NetworkInterface intf = en.nextElement();
           for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
           InetAddress inetAddress = enumIpAddr.nextElement();
                if (!inetAddress.isLoopbackAddress()) {
                return inetAddress.getHostAddress();
                }
           }
       }
       } catch (Exception ex) {
          Log.e("IP Address", ex.toString());
      }
      return null;
}

在清单文件中添加以下权限。

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

愉快的编码!


8
这个返回的值是错误的,类似于:"fe80::f225:b7ff:fe8c:d357%wlan0"。 - Jorgesys
@Jorgesys请查看evertvandenbruel的答案,他已经添加了inetAddress instanceof Inet4Address。 - temirbek
4
将 if 条件更改为以下内容以获取正确的 IP 地址:if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address)。 - Rajesh.k
代码仅返回第一个IP地址,手机可以同时拥有蜂窝、WIFI和BT地址。 - reker
如果您打开了热点,可能会获得多个IP地址。 - Harsha

30

Kotlin的极简版本

fun getIpv4HostAddress(): String {
    NetworkInterface.getNetworkInterfaces()?.toList()?.map { networkInterface ->
        networkInterface.inetAddresses?.toList()?.find {
            !it.isLoopbackAddress && it is Inet4Address
        }?.let { return it.hostAddress }
    }
    return ""
}

18

2
需要联网才能使用。这是一个大问题。 - David
6
为什么这是一个大问题?当然,你需要互联网连接,因为IP地址与这样的连接有技术关联。如果你离开家去餐厅,你将使用另一个互联网连接,因此使用另一个IP地址。你不需要添加类似 ACCESS_NETWORK_STATE 或 ACCESS_WIFI_STATE 的其他内容。互联网连接是我所提供的解决方案所需的唯一权限。 - Daan
2
哪个域名?如果ip-api.com无法使用,您可以使用telize.com作为备用。否则,您可以使用https://api.ipify.org/。它也在这里提供(不是json):http://ip.jsontest.com/?callback=showIP。许多应用程序使用的域名不能保证在线;这是正常的。但是,如果您使用备用选项,那么出现问题的可能性就非常小了。 - Daan
3
大卫最初的观点仍然成立。假设你在一个没有接入互联网的内部网络上,会怎么样呢? - hiandbaii
2
我从未考虑过这个问题,因为我不知道有哪些应用程序确实需要网络但又必须在没有互联网的情况下工作(也许有,但我认为对于移动设备来说并不适用)。 - Daan
显示剩余4条评论

10
private InetAddress getLocalAddress()throws IOException {

            try {
                for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                    NetworkInterface intf = en.nextElement();
                    for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                        InetAddress inetAddress = enumIpAddr.nextElement();
                        if (!inetAddress.isLoopbackAddress()) {
                            //return inetAddress.getHostAddress().toString();
                            return inetAddress;
                        }
                    }
                }
            } catch (SocketException ex) {
                Log.e("SALMAN", ex.toString());
            }
            return null;
        }

1
这个程序能否返回Wi-Fi接口的私有网络IP地址,例如192.168.0.x?还是它总是返回在互联网上使用的外部IP地址? - Ben H

9

getDeviceIpAddress方法返回设备的IP地址,并优先考虑已连接的wifi接口地址。

  @NonNull
    private String getDeviceIpAddress() {
        String actualConnectedToNetwork = null;
        ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connManager != null) {
            NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
            if (mWifi.isConnected()) {
                actualConnectedToNetwork = getWifiIp();
            }
        }
        if (TextUtils.isEmpty(actualConnectedToNetwork)) {
            actualConnectedToNetwork = getNetworkInterfaceIpAddress();
        }
        if (TextUtils.isEmpty(actualConnectedToNetwork)) {
            actualConnectedToNetwork = "127.0.0.1";
        }
        return actualConnectedToNetwork;
    }

    @Nullable
    private String getWifiIp() {
        final WifiManager mWifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        if (mWifiManager != null && mWifiManager.isWifiEnabled()) {
            int ip = mWifiManager.getConnectionInfo().getIpAddress();
            return (ip & 0xFF) + "." + ((ip >> 8) & 0xFF) + "." + ((ip >> 16) & 0xFF) + "."
                    + ((ip >> 24) & 0xFF);
        }
        return null;
    }


    @Nullable
    public String getNetworkInterfaceIpAddress() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                NetworkInterface networkInterface = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = networkInterface.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
                        String host = inetAddress.getHostAddress();
                        if (!TextUtils.isEmpty(host)) {
                            return host;
                        }
                    }
                }

            }
        } catch (Exception ex) {
            Log.e("IP Address", "getLocalIpAddress", ex);
        }
        return null;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接