Android网络相关工具类

(1)配置网络相关权限

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

(2)判断网络是否连接

/**
 * 判断网络是否连接
 * @return
 */
public boolean isConnected() {
    ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
    return mNetworkInfo != null && mNetworkInfo.isConnected();
}

这里只需要注意一点,isAvailable方法已经过时了,所以就不要使用了,将isAvailable改成isConnected即可。

12648131-d03fba2c5c3fa53e.png
图片.png

(3)判断当前网络是否流通
Android提供的API可以监听网络变化,可以获取网络状态,但是却无法得知网络是否流通,这里采用ping IP的方式查看网络是否流通,这里需要用到另一个工具类:Shell命令工具类

/**
 * 判断网络是否可用
 * @param ipORdomain
 * @return
 */
public boolean isAvailableByPing(String ipORdomain) {
    if (TextUtils.isEmpty(ipORdomain)) {
        return false;
    }
    ShellUtil.CommandResult result = ShellUtil.execCmd(String.format("ping -c 1 %s", ipORdomain), false);
    boolean ret = result.result == 0;
    if (result.successMsg != null) {
        Log.d("yunchong", "isAvailableByPing() called" + result.successMsg);
    }
    if (result.errorMsg != null) {
        Log.d("yunchong", "isAvailableByPing() called" + result.errorMsg);
    }
    return ret;
}

在AS上手动输入shell命令,结果如下

12648131-72c57c9d5cb63ccc.png
图片.png

不管是代码还是手动输入shell,返回的结果都是一样的,也就是说通过ping可以判断网络是否流通。
另外,需要强调的是,ping属于耗时操作,强烈建议在子线程中运行。

(4)判断WIFI是否连接(不能保证有网络流通)

/**
 * 判断 wifi 是否连接
 *
 * @return {@code true}: 连接<br>{@code false}: 未连接
 */
public boolean isWifiConnected() {
    ConnectivityManager mConnectivityManager = (ConnectivityManager) applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo mWiFiNetworkInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
    if (mWiFiNetworkInfo != null) {
        return mWiFiNetworkInfo.isConnected();
    }
    return false;
}

/**
 * 判断 wifi 是否连接
 *
 * @return {@code true}: 连接<br>{@code false}: 未连接
 */
public boolean isWifiConnected() {
    ConnectivityManager cm = (ConnectivityManager) applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
    return cm != null && cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI;
}

经过测试:

  • 当连接WIFI时,并且有网络时,返回true,当从WIFI切换到数据流量,那么返回false(没毛病)
  • 当连接其他手机开的热点时,并且其他手机的数据流量处于关闭状态,那么依然返回ture;

测试之后的结论是,该方案不可取。

(5)仅仅判断手机WIFI开关是否打开

/**
 * 判断 wifi 是否打开
 * @return {@code true}: 是<br>{@code false}: 否
 */
public boolean isWifiEnabled() {
    @SuppressLint("WifiManagerLeak")
    WifiManager wifiManager = (WifiManager) applicationContext.getSystemService(Context.WIFI_SERVICE);
    return wifiManager.isWifiEnabled();
}

(6)用代码打开或关闭WIFI开关

/**
 * 打开或关闭 wifi
 *
 * @param enabled {@code true}: 打开<br>{@code false}: 关闭
 */
public void setWifiEnabled(boolean enabled) {
    @SuppressLint("WifiManagerLeak")
    WifiManager wifiManager = (WifiManager) applicationContext.getSystemService(Context.WIFI_SERVICE);
    if (enabled) {
        if (!wifiManager.isWifiEnabled()) {
            wifiManager.setWifiEnabled(true);
        }
    } else {
        if (wifiManager.isWifiEnabled()) {
            wifiManager.setWifiEnabled(false);
        }
    }
}

(7)判断WIFI是否可用(结合3和5)(最终方案)

/**
 * 判断 wifi 数据是否可用
 *
 * @return {@code true}: 是<br>{@code false}: 否
 */
public boolean isWifiAvailable() {
    return isWifiEnabled() && isAvailableByPing("www.baidu.com");
}

在WIFI开关打开的情况下,再ping一下,如果都是ture才是真正的WIFI可用。

(8)判断移动数据网络是否连接(不能保证网络是否流通)

/**
 * 判断MOBILE网络是否连接
 * @param context
 * @return
 */
public boolean isMobileConnected(Context context) {
    if (context != null) {
        ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo mMobileNetworkInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
        if (mMobileNetworkInfo != null) {
            return mMobileNetworkInfo.isConnected();
        }
    }
    return false;
}

和上面WIFI开关打开之后不一定会有网络流通的原理是一样的,打开数据流量开关,但是不一定有网络流通,该方法有歧义,所以建议不采用。

(9)判断移动数据是否打开

/**
 * 判断移动数据是否打开
 *
 * @return {@code true}: 是<br>{@code false}: 否
 */
public boolean isMobileDataEnabled() {
    try {
        TelephonyManager tm = (TelephonyManager) applicationContext.getSystemService(Context.TELEPHONY_SERVICE);
        Method getMobileDataEnabledMethod = tm.getClass().getDeclaredMethod("getDataEnabled");
        if (getMobileDataEnabledMethod != null) {
            return (boolean) getMobileDataEnabledMethod.invoke(tm);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return false;
}

(10)打开或关闭移动数据开关

/**
 * 打开或关闭移动数据
 *
 * @param enabled {@code true}: 打开<br>{@code false}: 关闭
 */
public void setMobileDataEnabled(boolean enabled) {

    try {
        TelephonyManager tm = (TelephonyManager) applicationContext.getSystemService(Context.TELEPHONY_SERVICE);
        Method setMobileDataEnabledMethod = tm.getClass().getDeclaredMethod("setDataEnabled", boolean.class);
        if (null != setMobileDataEnabledMethod) {
            setMobileDataEnabledMethod.invoke(tm, enabled);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

测试之后,发现这个方法无效,看了源码之后发现反射的方法名和参数是没有问题的,源码如下:

/**
 * Turns mobile data on or off.
 * If this object has been created with {@link #createForSubscriptionId}, applies to the given
 * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
 *
 * <p>Requires Permission:
 * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
 * app has carrier privileges (see {@link #hasCarrierPrivileges}).
 *
 * @param enable Whether to enable mobile data.
 *
 */
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setDataEnabled(boolean enable) {
    setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
}

但是源码中多了一个“修改网络状态”的权限,这个权限在Android 5.0之后就升级为系统权限,普通的app是没有权限修改手机状态的,解决方案是,将app打上系统签名,怎么打上系统签名就不描述了,我再网上找了一个参考博客:Apk 使用系统签名

(11)获取网络运营商名称

/**
 * 获取网络运营商名称
 * <p>中国移动、如中国联通、中国电信</p>
 *
 * @return 运营商名称
 */
public String getNetworkOperatorName() {
    TelephonyManager tm = (TelephonyManager) applicationContext.getSystemService(Context.TELEPHONY_SERVICE);
    return tm != null ? tm.getNetworkOperatorName() : null;
}

(12)获取当前网络类型

/**
 * 网络类型
 */
public enum NetworkType {

    // wifi
    NETWORK_WIFI,
    // 4G 网
    NETWORK_4G,
    // 3G 网
    NETWORK_3G,
    // 2G 网
    NETWORK_2G,
    // 未知网络
    NETWORK_UNKNOWN,
    // 没有网络
    NETWORK_NO
}


/**
 * 获取当前网络类型
 *
 * @return 网络类型
 */
public NetworkType getNetworkType() {
    NetworkType netType = NetworkType.NETWORK_NO;
    ConnectivityManager mConnectivityManager = (ConnectivityManager) applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
    if (mNetworkInfo != null && mNetworkInfo.isAvailable()) {
        if (mNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
            netType = NetworkType.NETWORK_WIFI;
        } else if (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
            switch (mNetworkInfo.getSubtype()) {
                case TelephonyManager.NETWORK_TYPE_GSM:
                case TelephonyManager.NETWORK_TYPE_GPRS:
                case TelephonyManager.NETWORK_TYPE_CDMA:
                case TelephonyManager.NETWORK_TYPE_EDGE:
                case TelephonyManager.NETWORK_TYPE_1xRTT:
                case TelephonyManager.NETWORK_TYPE_IDEN:
                    netType = NetworkType.NETWORK_2G;
                    break;

                case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
                case TelephonyManager.NETWORK_TYPE_EVDO_A:
                case TelephonyManager.NETWORK_TYPE_UMTS:
                case TelephonyManager.NETWORK_TYPE_EVDO_0:
                case TelephonyManager.NETWORK_TYPE_HSDPA:
                case TelephonyManager.NETWORK_TYPE_HSUPA:
                case TelephonyManager.NETWORK_TYPE_HSPA:
                case TelephonyManager.NETWORK_TYPE_EVDO_B:
                case TelephonyManager.NETWORK_TYPE_EHRPD:
                case TelephonyManager.NETWORK_TYPE_HSPAP:
                    netType = NetworkType.NETWORK_3G;
                    break;
                case TelephonyManager.NETWORK_TYPE_IWLAN:
                case TelephonyManager.NETWORK_TYPE_LTE:
                    netType = NetworkType.NETWORK_4G;
                    break;
                default:

                    String subtypeName = mNetworkInfo.getSubtypeName();
                    //  中国移动 联通 电信 三种 3G 制式
                    if (subtypeName.equalsIgnoreCase("TD-SCDMA") || subtypeName.equalsIgnoreCase("WCDMA") || subtypeName.equalsIgnoreCase("CDMA2000")) {
                        netType = NetworkType.NETWORK_3G;
                    } else {
                        netType = NetworkType.NETWORK_UNKNOWN;
                    }
                    break;
            }
        } else {
            netType = NetworkType.NETWORK_UNKNOWN;
        }
    }
    return netType;
}

(13)通过WIFI获取IP地址

/**
 * 通过 wifi 获取本地 IP 地址
 *
 * @return IP 地址
 */
public String getIpAddressByWifi() {
    // 获取wifi服务
    WifiManager wifiManager = (WifiManager) applicationContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
    // 判断wifi是否开启
    if (!wifiManager.isWifiEnabled()) {
        wifiManager.setWifiEnabled(true);
    }
    WifiInfo wifiInfo = wifiManager.getConnectionInfo();
    int ipAddress = wifiInfo.getIpAddress();
    return intToIp(ipAddress);
}


private String intToIp(int i) {
    return (i & 0xFF) + "." +
            ((i >> 8) & 0xFF) + "." +
            ((i >> 16) & 0xFF) + "." +
            (i >> 24 & 0xFF);
}

(14)获取IPv4和IPv6

/**
 * 获取 IP 地址
 *
 * @param useIPv4 是否用 IPv4
 * @return useIPv4为true返回IPv4,为false返回IPv6
 */
public String getIPAddress(boolean useIPv4) {
    try {
        for (Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces(); nis.hasMoreElements(); ) {
            NetworkInterface ni = nis.nextElement();
            // 防止小米手机返回 10.0.2.15
            if (!ni.isUp()) continue;
            for (Enumeration<InetAddress> addresses = ni.getInetAddresses(); addresses.hasMoreElements(); ) {
                InetAddress inetAddress = addresses.nextElement();
                if (!inetAddress.isLoopbackAddress()) {
                    String hostAddress = inetAddress.getHostAddress();
                    boolean isIPv4 = hostAddress.indexOf(':') < 0;
                    if (useIPv4) {
                        if (isIPv4) return hostAddress;
                    } else {
                        if (!isIPv4) {
                            int index = hostAddress.indexOf('%');
                            return index < 0 ? hostAddress.toUpperCase() : hostAddress.substring(0, index).toUpperCase();
                        }
                    }
                }
            }
        }
    } catch (SocketException e) {
        e.printStackTrace();
    }
    return null;
}

(15)根据域名获取IP

/**
 * 根据域名获取IP
 * @param domain 域名
 * @return IP 地址
 */
public String getDomainAddress(@Nullable String domain) {
    InetAddress inetAddress;
    try {
        inetAddress = InetAddress.getByName(domain);
        return inetAddress.getHostAddress();
    } catch (UnknownHostException e) {
        e.printStackTrace();
        return null;
    }
}

需要注意的是:这个方法必须在子线程中运行,否则报错

android.os.NetworkOnMainThreadException
    at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1145)
    at java.net.InetAddress.lookupHostByName(InetAddress.java:385)
    at java.net.InetAddress.getAllByNameImpl(InetAddress.java:236)
    at java.net.InetAddress.getByName(InetAddress.java:289)
    at com.jiangxi.login.networkdemo.NetUtil.getDomainAddress(NetUtil.java:310)

大家可以把以上的15个方法放在一个工具类中。

猜你喜欢

转载自blog.csdn.net/weixin_33744141/article/details/87189398