(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即可。
(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命令,结果如下
不管是代码还是手动输入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个方法放在一个工具类中。