Android 检测切换网络状态

最近项目过程中遇到一个问题,在部分机型上,切换网络状态或者网络状态不好时会跳的比较频繁。UI层对应做出改变的交互就不太好了,尤其对于联网要重新请求接口的需求来说,会非常蛋疼。。。
有时候手机一开始使用wifi连接,当进入待机一段时间后,会自动从Wifi切换到GPRS网络,
如果编写网络程序,网络自动切换对程序的影响是非常明显的,IP地址肯定会变化。

首先,举个例子,一般我们在检测网络状态时的代码这样子的:

1.检查是否存在:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 检查网络
*
* @param context
* @return
*/

public static boolean checkNetState(Context context) {
boolean netstate = false;
ConnectivityManager connectivity = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity != null) {
NetworkInfo[] info = connectivity.getAllNetworkInfo();
if (info != null) {
for (int i = 0; i < info.length; i++) {
if (info[i].getState() == NetworkInfo.State.CONNECTED) {
netstate = true;
break;
}
}
}
}
return netstate;
}

2.判断网络状态(wifi gprs 无网络):

1
2
3
4
5
6
7
8
9
10
11
12
public static int getConnectivityStatus(Context context) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (null != activeNetwork) {
if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI)
return TYPE_WIFI;
if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE)
return TYPE_MOBILE;
}
return TYPE_NOT_CONNECTED;
}

举例一种情况,当wifi和gprs都是打开状态下,自己写个BroadcastReceiver测试一下:

1
2
3
4
5
6
@Override
public void onReceive(final Context context, final Intent intent) {
/* … */
int status = NetworkUtil.getConnectivityStatus(context);
EventBus.getDefault().post(new EventNetworkChange(status));
}

关闭wifi,会发现status首先得到的是无网络,随后马上变成了gprs,对于之前说的那些有需求的童鞋就比较蛋疼了。回到wifi和gprs都打开的情况,关闭gprs,发现网络状态不做任何改变,可能连上wifi后,gprs的基站连接就已经关闭了,毕竟wifi优先嘛~
ok,wifi是关闭,gprs都是打开状态下,这时候打开wifi,可看到onReceive被接收了两次,第一次接收到了有网络,第二次还是有网络,第一次的这时候wifi还有连上,网络状态为gprs,第二次WIFI连接上,又receive到了,连续两次的拿到网络,如果在分发的事件里要做网络调用的童鞋就JJ了。。。。(难道你要用排重么?哈哈)

遇到这个问题的时候,我去google了下,没有找到比较好的解决方案。一般来说在onReceive 接收到时候,通过 intent.getExtras() 能提到详细的网络变化参数信息,例如: 断网 or 移动网络上线 or wifi网络上线 or 移动网络与wifi切换

1
2
3
4
5
networkInfo=(NetworkInfo)intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); //当前的
otherNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO); //另一个网络状态
intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); //网络是否全部断开
intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false); //wifi和gprs是否在切换
intent.getStringExtra(ConnectivityManager.EXTRA_REASON); //网络状态改变的原因

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private int previousNetType = -1; //变化之前的网络状态
private boolean isNetOnLine;
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
NetworkInfo networkInfo = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
NetworkInfo otherNetworkInfo = intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
//String reason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON); //网络状态改变的原因
boolean isFailover = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false); //wifi和3g是否在切换
boolean isNoConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); //网络是否全部断开
if (isNoConnectivity) {
previousNetType = -1;
isNetOnLine = false;
ToastUtil.show(context, "网络已断开");
} else {
isNetOnLine = true;
if (isFailover || otherNetworkInfo == null) {
if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
if (previousNetType != ConnectivityManager.TYPE_WIFI && networkInfo.isConnected()) {
previousNetType = ConnectivityManager.TYPE_WIFI;
ToastUtil.show(context, "已连接到wifi");
}
} else {
if (previousNetType == ConnectivityManager.TYPE_WIFI || previousNetType == -1) {
if (networkInfo.isConnected()) {
previousNetType = ConnectivityManager.TYPE_MOBILE;
ToastUtil.show(context, "已连接到gprs");
}
}
}
}
}
}
}

ok,看似好像解决了问题,具体可以测试一下。但是在看到这段的时候感觉好啰嗦,好啰嗦,好啰嗦。。。
于是 暴力 解决方案来了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private int currentStatus = -1;
private Handler handler;
@Override
public void onReceive(final Context context, final Intent intent) {
if (handler==null)
handler = new Handler();
handler.removeMessages(Message.obtain().what);
handler.postDelayed(() -> {
int status = NetworkUtil.getConnectivityStatus(context);
if (status != currentStatus) {
currentStatus = status;
EventBus.getDefault().post(new EventNetworkChange(status));
}
handler.removeMessages(Message.obtain().what);
handler = null;
},2000);
}

目前用下来暂时没有发现什么问题。。。

嗯,就这样。。。