Android獲取設(shè)備IP地址代碼與分析

一直以來,好像沒有一段標(biāo)準(zhǔn)的代碼能提供Android設(shè)備此刻的IP地址,究其原因,Android設(shè)備的網(wǎng)卡可能不只一個(gè),如蜂窩網(wǎng)卡、WiFi網(wǎng)卡,而且同一個(gè)網(wǎng)卡也可能擁有不止一個(gè)IP地址。基于此,一個(gè)Android終端很有可能同時(shí)擁有多個(gè)IP地址(不只是同時(shí)擁有IPv4和IPv6地址),比如開啟熱點(diǎn)共享蜂窩網(wǎng)絡(luò)的時(shí)候,蜂窩網(wǎng)卡擁有一個(gè)IPv4地址來訪問外網(wǎng),WiFi網(wǎng)卡擁有一個(gè)IPv4地址來作為內(nèi)網(wǎng)的網(wǎng)關(guān)。

網(wǎng)上比較流行的獲取Android設(shè)備IP地址的代碼有以下幾種,下面我們來一一分析一下。

1. 不可行的方法

String ipAddress = Inet4Address.getLocalHost().getHostAddress()

這個(gè)是Java提供的API,在Android上執(zhí)行需要以下權(quán)限(經(jīng)測(cè)試Android版本6.0.1的一部機(jī)器不需要該權(quán)限,比較納悶,求解答)

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

此外,由于該方法使用了網(wǎng)絡(luò)通信,因此不能在UI線程執(zhí)行。

該方法顧名思義是獲取本地主機(jī)的IP地址,在某些Java平臺(tái)上可以得到想要的結(jié)果,但是我截取了Android官方給出的關(guān)于該方法的部分說明如下:

Returns an InetAddress for the local host if possible, or the loopback address otherwise. This method works by getting the hostname, performing a DNS lookup, and then taking the first returned address.
Note that if the host doesn't have a hostname set – as Android devices typically don't – this method will effectively return the loopback address, albeit by getting the name localhost and then doing a lookup to translate that to 127.0.0.1.

可以看出,一般在Android平臺(tái)上,由于網(wǎng)絡(luò)通信設(shè)備沒有設(shè)置hostname,因此無法進(jìn)行DNS檢索得到其相應(yīng)的IP地址,因此該方法會(huì)返回本地回環(huán)地址,即127.0.0.1,也就是說這個(gè)方法在Android平臺(tái)上無法達(dá)到我們一般的獲取本機(jī)IP地址的目的,經(jīng)過測(cè)試,結(jié)果也確實(shí)如此。

2. 部分可行的方法

WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
int ipAddressInt = wm.getConnectionInfo().getIpAddress();
String ipAddress = String.format(Locale.getDefault(), "%d.%d.%d.%d", (ipAddressInt & 0xff), (ipAddressInt >> 8 & 0xff), (ipAddressInt >> 16 & 0xff), (ipAddressInt >> 24 & 0xff));

方法執(zhí)行所需權(quán)限為:

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

需要說明的是,上述代碼第二行返回的是一個(gè)int類型的值,如1795336384,它對(duì)應(yīng)的十六進(jìn)制值6b02a8c0每?jī)晌槐銓?duì)應(yīng)IPv4地址的每一項(xiàng)(逆序,如c0轉(zhuǎn)化為十進(jìn)制為192)。

經(jīng)測(cè)試,通過該方法可以獲得當(dāng)前WiFi網(wǎng)絡(luò)中Android設(shè)備的IPv4地址,但是顯然,該方法是通過WifiManager獲取當(dāng)前網(wǎng)絡(luò)連接下的IP地址的,因此它只局限于使用WiFi網(wǎng)絡(luò)的情況,當(dāng)使用蜂窩等其他網(wǎng)絡(luò)設(shè)備時(shí),該方法無效,會(huì)返回0值。另外,如果你是通過比較hacker的方式比如沒有通過系統(tǒng)Framework層打開WiFi,而是自己通過Linux命令創(chuàng)建的WiFi網(wǎng)絡(luò),那么像這種Framework層提供的API也是不起作用的。

3. 基本可行的方法

    public static String getIpAddressString() {
        try {
            for (Enumeration<NetworkInterface> enNetI = NetworkInterface
                    .getNetworkInterfaces(); enNetI.hasMoreElements(); ) {
                NetworkInterface netI = enNetI.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = netI
                        .getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (inetAddress instanceof Inet4Address && !inetAddress.isLoopbackAddress()) {
                        return inetAddress.getHostAddress();
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }
        return "";
    }

方法執(zhí)行所需權(quán)限為:

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

這段代碼不難理解,其實(shí)就是雙重循環(huán)獲取終端中所有網(wǎng)絡(luò)接口的所有IP地址,然后返回第一個(gè)遇到的非本地回環(huán)的IPv4地址。這種方式可以很好的覆蓋我們一般的需求。根據(jù)Android系統(tǒng)的運(yùn)行機(jī)制,當(dāng)WiFi網(wǎng)絡(luò)開啟時(shí)蜂窩網(wǎng)絡(luò)會(huì)自動(dòng)關(guān)閉,因此遍歷到的第一個(gè)地址是WiFi網(wǎng)卡的IP地址;同樣,當(dāng)關(guān)閉WiFi網(wǎng)絡(luò),打開蜂窩網(wǎng)絡(luò)時(shí),遍歷到的第一個(gè)地址是蜂窩網(wǎng)卡的IP地址。

那么,為什么我叫這種方式為基本可行的方法呢,因?yàn)樗祷氐慕Y(jié)果并不是百分百“正確”的,確切地說并不一定是開發(fā)人員想要的結(jié)果。比如當(dāng)Android手機(jī)開啟熱點(diǎn)的時(shí)候,實(shí)際上是通過WiFi網(wǎng)卡共享其蜂窩網(wǎng)絡(luò),因此此時(shí),WiFi網(wǎng)卡和蜂窩網(wǎng)卡分配了不同的IP地址,但由于蜂窩網(wǎng)卡對(duì)應(yīng)的NetworkInterface對(duì)象出現(xiàn)的位置要先于WiFi網(wǎng)卡,因此該方法返回的實(shí)際上是蜂窩網(wǎng)卡的IP地址。如果想要始終獲取WiFi網(wǎng)卡的IP地址可以在上述的兩個(gè)循環(huán)間添加如下篩選代碼:

if (netI.getDisplayName().equals("wlan0") || netI.getDisplayName().equals("eth0"))

其中"wlan0"和"eth0"為常見的WLAN網(wǎng)卡的DisplayName名稱,絕大部分為"wlan0",比較老的機(jī)型可能會(huì)是"eth0"或其他。

這里只是舉了一個(gè)簡(jiǎn)單的例子,其實(shí)還有很多特殊的情況,比如開啟USB網(wǎng)絡(luò)共享的情況、開啟網(wǎng)絡(luò)代理的情況、之前提到的Hacker手段同時(shí)打開蜂窩網(wǎng)絡(luò)和WiFi網(wǎng)絡(luò)(非WiFi熱點(diǎn))的情況等等,這些網(wǎng)絡(luò)環(huán)境下都會(huì)存在多IP的情況,因此該方法不一定完全適用了。

正如文章開頭所說,由于一個(gè)Android設(shè)備同一時(shí)刻可能不只有一個(gè)IP地址,因此可以說沒有任何一段通用的代碼能獲取每個(gè)人心中想要獲取的IP地址,重要的還是根據(jù)自己具體的需求來進(jìn)行相應(yīng)的代碼修改,通過對(duì)獲取的IP地址列表進(jìn)行篩選來得到想要的結(jié)果。

本文的討論是圍繞IPv4地址的,如果想要獲取IPv6地址,Android API也提供了相應(yīng)的類或方法,只需要在上述代碼的基礎(chǔ)上作出微小修改即可。

最后附上在StackOverFlow上看到的關(guān)于IP地址篩選的總結(jié),供大家參考。

  • Any address in the range 127.xxx.xxx.xxx is a "loopback" address. It is only visible to "this" host.
  • Any address in the range 192.168.xxx.xxx is a private (aka site local) IP address. These are reserved for use within an organization. The same applies to 10.xxx.xxx.xxx addresses, and 172.16.xxx.xxx through 172.31.xxx.xxx.
  • Addresses in the range 169.254.xxx.xxx are link local IP addresses. These are reserved for use on a single network segment.
  • Addresses in the range 224.xxx.xxx.xxx through 239.xxx.xxx.xxx are multicast addresses.
  • The address 255.255.255.255 is the broadcast address.
  • Anything else should be a valid public point-to-point IPv4 address.

文中所有代碼可以在個(gè)人github主頁查看和下載。

另,個(gè)人技術(shù)博客,同步更新,歡迎關(guān)注!轉(zhuǎn)載請(qǐng)注明出處!文中若有什么錯(cuò)誤希望大家探討指正!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容