一箫一剑平生意,负尽狂名十五年。这篇文章主要讲述android 监听网络状态的变化及实战相关的知识,希望能为你提供帮助。
本篇文章已授权微信公众号 guolin_blog (
郭霖)
独家发布
转载请注明博客地址:
http://blog.csdn.net/gdutxiaoxu/article/details/53008266
平时我们在请求错误的情况下,
通常会进行处理一下,
一般来说,
主要分为两方面的错误
- 没有网络的错误
- 在有网络的情况下, 我们客户端的错误或者服务器端的错误
- 怎样监听网络状态的变化, 包括是否打开WiFi, 否打开数据网络, 当前连接的网络是否可用
- 网络没有打开情况下的处理,如弹出对话框, 跳转到 打开 WiFi设置的界面等
- 非WiFi情况下是否加载图片, 是否播放视频等
文章图片
实现思路 在网络错误的情况下获取网络状态进行判断, 这种方法是可行的, 但你想过了没有, 如果每次都要进行这样的判断, 岂不是代码量很多? 有人会说, 那把代码封装到一个类不就好了吗? 这样确实能减少代码量, 但是每次都要主动去获取, 这样是不是挺麻烦的。实际上, google 早就帮我们封装好了, 在网络状态变化的情况下会发出广播, 我们只需要监听广播就好了 。
使用静态广播还是动态注册广播的方式好呢?
如果你不太清楚的话, 我只能说即 基础不扎实。我们的应用之所以要监听网络状态的 变化, 主要是为了在错误的情况下方便进行处理, 退出我们当前的应用之后当然不需要监听了, 所以选择动态注册广播。
- 动态注册: 随着所在的Activity或者应用销毁 以后, 不会受到该广播
- 静态注册: 退出应用后, 仍然能够收到相应的广播
通过广播监听网络状态的两种方法 静态注册
<
receiver android:name=
"
.network.NetworkConnectChangedReceiver"
>
<
intent-filter>
<
action android:name=
"
android.NET.conn.CONNECTIVITY_CHANGE"
/>
<
action android:name=
"
android.Net.wifi.WIFI_STATE_CHANGED"
/>
<
action android:name=
"
android.net.wifi.STATE_CHANGE"
/>
<
/intent-filter>
<
/receiver>
动态注册
第一步: 在AndroidMainest文件里面注册
<
receiver android:name=
"
.network.NetworkConnectChangedReceiver"
>
<
/receiver>
第二步: 调用 Context registerReceiver(Receiver,IntentFilter) 进行注册
IntentFilter filter =
new IntentFilter();
filter.addAction("
android.net.conn.CONNECTIVITY_CHANGE"
);
filter.addAction("
android.net.wifi.WIFI_STATE_CHANGED"
);
filter.addAction("
android.net.wifi.STATE_CHANGE"
);
registerReceiver(mNetworkChangeListener,filter);
我们自定义的NetworkChangeListener广播
/**
* 网络改变监控广播
* <
p>
* 监听网络的改变状态,只有在用户操作网络连接开关(wifi,mobile)的时候接受广播,
* 然后对相应的界面进行相应的操作,
并将 状态 保存在我们的APP里面
* <
p>
* <
p>
* Created by xujun
*/
public class NetworkConnectChangedReceiver extends BroadcastReceiver {private static final String TAG =
"
xujun"
;
public static final String TAG1 =
"
xxx"
;
@
Override
public void onReceive(Context context, Intent intent) {
// 这个监听wifi的打开与关闭,
与wifi的连接无关
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
int wifiState =
intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0);
Log.e(TAG1, "
wifiState"
+
wifiState);
switch (wifiState) {
case WifiManager.WIFI_STATE_DISABLED:
APP.getInstance().setEnablaWifi(false);
break;
case WifiManager.WIFI_STATE_DISABLING:break;
case WifiManager.WIFI_STATE_ENABLING:
break;
case WifiManager.WIFI_STATE_ENABLED:
APP.getInstance().setEnablaWifi(true);
break;
case WifiManager.WIFI_STATE_UNKNOWN:
break;
default:
break;
}
}
// 这个监听wifi的连接状态即是否连上了一个有效无线路由,
当上边广播的状态是WifiManager
// .WIFI_STATE_DISABLING,
和WIFI_STATE_DISABLED的时候,
根本不会接到这个广播。
// 在上边广播接到广播是WifiManager.WIFI_STATE_ENABLED状态的同时也会接到这个广播,
// 当然刚打开wifi肯定还没有连接到有效的无线
if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) {
Parcelable parcelableExtra =
intent
.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if (null !=
parcelableExtra) {
NetworkInfo networkInfo =
(NetworkInfo) parcelableExtra;
State state =
networkInfo.getState();
boolean isConnected =
state =
=
State.CONNECTED;
// 当然,
这边可以更精确的确定状态
Log.e(TAG1, "
isConnected"
+
isConnected);
if (isConnected) {
APP.getInstance().setWifi(true);
} else {
APP.getInstance().setWifi(false);
}
}
}
// 这个监听网络连接的设置,
包括wifi和移动数据的打开和关闭。.
// 最好用的还是这个监听。wifi如果打开,
关闭,
以及连接上可用的连接都会接到监听。见log
// 这个广播的最大弊端是比上边两个广播的反应要慢,
如果只是要监听wifi,
我觉得还是用上边两个配合比较合适
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
ConnectivityManager manager =
(ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
Log.i(TAG1, "
CONNECTIVITY_ACTION"
);
NetworkInfo activeNetwork =
manager.getActiveNetworkInfo();
if (activeNetwork !=
null) { // connected to the internet
if (activeNetwork.isConnected()) {
if (activeNetwork.getType() =
=
ConnectivityManager.TYPE_WIFI) {
// connected to wifi
APP.getInstance().setWifi(true);
Log.e(TAG, "
当前WiFi连接可用 "
);
} else if (activeNetwork.getType() =
=
ConnectivityManager.TYPE_MOBILE) {
// connected to the mobile provider'
s data plan
APP.getInstance().setMobile(true);
Log.e(TAG, "
当前移动网络连接可用 "
);
}
} else {
Log.e(TAG, "
当前没有网络连接,
请确保你已经打开网络 "
);
}Log.e(TAG1, "
info.getTypeName()"
+
activeNetwork.getTypeName());
Log.e(TAG1, "
getSubtypeName()"
+
activeNetwork.getSubtypeName());
Log.e(TAG1, "
getState()"
+
activeNetwork.getState());
Log.e(TAG1, "
getDetailedState()"
+
activeNetwork.getDetailedState().name());
Log.e(TAG1, "
getDetailedState()"
+
activeNetwork.getExtraInfo());
Log.e(TAG1, "
getType()"
+
activeNetwork.getType());
} else {// not connected to the internet
Log.e(TAG, "
当前没有网络连接,
请确保你已经打开网络 "
);
APP.getInstance().setWifi(false);
APP.getInstance().setMobile(false);
APP.getInstance().setConnected(false);
}}
}}
最后, 别忘记添加一下权限
<
uses-permission android:name=
"
android.permission.INTERNET"
/>
<
!--允许读取网络状态-->
<
uses-permission android:name=
"
android.permission.ACCESS_NETWORK_STATE"
>
<
/uses-permission>
<
!--允许读取wifi网络状态-->
<
uses-permission android:name=
"
android.permission.ACCESS_WIFI_STATE"
/>
思路解析
从上面的代码中, 我们可以知道我们将我们当前的网络状态保存在我们的 APP 里面, 这样当网络状态变化的时候会自动去改变 APP 里面相应的状态量, 我们进行网络处理的 时候只需要去获取 APP里面的状态量, 便可以判断出是属于哪一种网络错误, 是不是很方便呢。
至于广播的Action主要有三种类型:
WifiManager.WIFI_STATE_CHANGED_ACTION
这个监听wifi的打开与关闭, 与wifi的连接无关
WifiManager.NETWORK_STATE_CHANGED_ACTION:
这个监听wifi的连接状态即是否连上了一个有效无线路由, 当上边广播的状态是WifiManager.WIFI_STATE_DISABLING, 和WIFI_STATE_DISABLED的时候, 根本不会接到这个广播。
在上边广播接到广播是WifiManager.WIFI_STATE_ENABLED状态的同时也会接到这个广播, 当然刚打开wifi肯定还没有连接到有效的无线
ConnectivityManager.CONNECTIVITY_ACTION
这个监听网络连接的设置, 包括wifi和移动数据的打开和关闭。.
最好用的还是这个监听。wifi如果打开, 关闭, 以及连接上可用的连接都会接到监听。这个广播的最大弊端是比上边两个广播的反应要慢, 如果只是要监听wifi, 我觉得还是用上边两个配合比较合适。
至于这个ConnectivityManager, NetworkInfo是什么东西, 别急, 下面会大概介绍一下。
ConnectivityManager和NetworkInfo ConnectivityManager主要用来干什么
- Monitor network connections (Wi-Fi, GPRS, UMTS, etc.) ( 用来处理网络连接 , 包括Wi-Fi, GPRS, UMTS等)
- Send broadcast intents when network connectivity changes( 用 网络状态发生变化的时候发出 广播 )
- Attempt to “fail over” to another network when connectivity to a network is lost( 但断开网络连接的时候, 尝试去; 连接另外一个网络
- Provide an API that allows applications to query the coarse-grained or fine-grained state of the available networks(
- Provide an API that allows applications to request and select networks for their data traffic
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context
.CONNECTIVITY_SERVICE);
获取 NetworkInfo对象
主要有一下 几种方法
- getNetworkInfo(int networkType), 但是这个方法已经过时, 官网的解释如下: This method was deprecated in API level 23. This method does not support multiple connected networks of the same type. Use getAllNetworks() and getNetworkInfo(android.net.Network) instead.
- getNetworkInfo(Network network)
- getActiveNetwork()
Returns a Network object corresponding to the currently active default data network. - getActiveNetworkInfo(), Returns details about the currently active default data network.
- getAllNetworkInfo()这个方法已经过时, Use getAllNetworks() and getNetworkInfo(android.net.Network) instead.
第一种方法, 只不过在 API23的时候已经 过时了
State wifiState =
null;
State mobileState =
null;
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context
.CONNECTIVITY_SERVICE);
wifiState =
cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();
mobileState =
cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState();
Log.d(TAG1,
"
wifi状态:"
+
wifiState +
"
\\n mobile状态:"
+
mobileState);
if (wifiState !=
null &
&
mobileState !=
null
&
&
State.CONNECTED !=
wifiState
&
&
State.CONNECTED =
=
mobileState) {// 手机网络连接成功
Log.d(TAG1, "
手机2g/3g/4g网络连接成功"
);
APP.getInstance().setMobile(true);
APP.getInstance().setWifi(false);
APP.getInstance().setConnected(true);
} else if (wifiState !=
null &
&
State.CONNECTED =
=
wifiState) {// 无线网络连接成功
Log.d(TAG1, "
无线网络连接成功"
);
APP.getInstance().setMobile(false);
APP.getInstance().setWifi(true);
APP.getInstance().setConnected(true);
} else if (wifiState !=
null &
&
mobileState !=
null
&
&
State.CONNECTED !=
wifiState
&
&
State.CONNECTED !=
mobileState) {// 手机没有任何的网络
Log.d(TAG1, "
手机没有任何的网络"
);
APP.getInstance().setMobile(false);
APP.getInstance().setWifi(false);
APP.getInstance().setConnected(false);
}
【android 监听网络状态的变化及实战】第二种方法
ConnectivityManager manager =
(ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
Log.i(TAG1, "
CONNECTIVITY_ACTION"
);
NetworkInfo activeNetwork =
manager.getActiveNetworkInfo();
if (activeNetwork !=
null) { // connected to the internet
if (activeNetwork.isConnected()) {
if (activeNetwork.getType() =
=
ConnectivityManager.TYPE_WIFI) {
// connected to wifi
APP.getInstance().setWifi(true);
Log.e(TAG, "
当前WiFi连接可用 "
);
} else if (activeNetwork.getType() =
=
ConnectivityManager.TYPE_MOBILE) {
// connected to the mobile provider'
s data plan
APP.getInstance().setMobile(true);
Log.e(TAG, "
当前移动网络连接可用 "
);
}
} else {
Log.e(TAG, "
当前没有网络连接,
请确保你已经打开网络 "
);
}
} else {// not connected to the internet
Log.e(TAG, "
当前没有网络连接,
请确保你已经打开网络 "
);
APP.getInstance().setWifi(false);
APP.getInstance().setMobile(false);
APP.getInstance().setConnected(false);
}
网络错误情况的处理 正如前面所提到的, 这篇博客吧网络错误主要分为两大类
- 没有网络情况的错误
- 在有网络的情况下,
我们客户端的错误或者服务器端的错误
在这里 我们主要处理没有网络情况下的错误, 现在 个人了解到 的主要有两种处理方法。
在APP启动的 时候检查当前是否已经连接上网络, 弹出一个对话框没有的话跳转到设置界面或者WiFi设置界面或者打开移动网络界面 。
第二种方法
其实跟第一种做法差不多, 只是在每一次 错误的情况下, 都会判断当前有没有 网络 , 没有弹出一个对话框, 跳转到设置界面或者WiFi设置界面或者打开移动网络界面 , 下面我们我们一起来看一下 怎样弹出一个对话框, 并且跳转到相应的设置界面
这里我们采取第一种做法, 效果图如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-soHQMd4i-1617723276312)(https://raw.githubusercontent.com/gdutxiaoxu/blog_pic/master/19_05/9fe4afa0gw1f9dp8oqfc2g208u0gjh6x.gif)]
代码如下
public static void showWifiDlg(final Context context) {
AlertDialog.Builder builder =
new AlertDialog.Builder(context.getApplicationContext());
if (mWifiDialog =
=
null) {
mWifiDialog =
builder.setIcon(R.drawable.ic_launcher)//
.setTitle("
wifi设置"
)//
.setMessage("
当前无网络"
).setPositiveButton("
设置"
, new DialogInterface
.OnClickListener() {@
Override
public void onClick(DialogInterface dialog, int which) {
// 跳转到系统的网络设置界面
Intent intent =
null;
// 先判断当前系统版本
if (android.os.Build.VERSION.SDK_INT >
10) {// 3.0以上
intent =
new Intent(android.provider.Settings
.ACTION_WIFI_SETTINGS);
} else {
intent =
new Intent();
intent.setClassName("
com.android.settings"
,
Settings.ACTION_WIFI_SETTINGS);
}
if ((context instanceof Application)) {
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
}
}).setNegativeButton("
知道了"
, null).create();
// 设置为系统的Dialog,
这样使用Application的时候不会 报错
mWifiDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
}
mWifiDialog.show();
}
这里对几个 重要的 Action说一下
- ACTION_DATA_ROAMING_SETTINGS : 跳转到移动网络设置界面
- ACTION_WIFI_SETTINGS
Activity Action: Show settings to allow configuration of Wi-Fi. - ACTION_WIRELESS_SETTINGS
Activity Action: Show settings to allow configuration of wireless controls such as Wi-Fi, Bluetooth and Mobile networks.
需要注意的是
若我们 使用的Context不是Activity 的Context 而是Application的 Context, 我们 需要做以下处理 , 否则会报错
// 设置为系统级别的Dialog
mWifiDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
if ((context instanceof Application)) {intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
在AndroidMainFest中添加以下权限 。
<
!--允许 弹出系统级别的AlterDialog-->
<
uses-permission android:name=
"
android.permission.SYSTEM_ALERT_WINDOW"
/>
智能无图 智能无图, 这种浏览模式我们平时 很常见, 比如在UC浏览器中, 网易新闻中都有看到这种模式, 这种模式的实质就是监听网络状态, 再根据是否是WiFi去确定是否加载网络图片。
效果图如下
我们可以看到在开启智能无图的情况下, 若不是连接WiFi , 我们是不会去加载网络图片的。
文章图片
实现的核心代码如下
1) 当智能无图模式变化的时候, 我们会把标志存进SharePreferences中
mSwitchWifiPic.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@
Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {SPUtils.put(SPConstants.isIntelligentNoPic,isChecked);
}
});
同时我们为了进来的时候界面与SharePreferences中的 isIntelligentNoPic的值保持一致, 我们需要调用一下代码
boolean isIntelligentNoPic =
SPUtils.getBoolean(SPConstants.isIntelligentNoPic);
mSwitchWifiPic.setChecked(isIntelligentNoPic);
2) 在NewsListAdapter中
// 是否开启智能无图模式,
true表示开启智能无图模式
boolean isIntelligentNoPic =
SPUtils.getBoolean(Constants.SPConstants.isIntelligentNoPic);
WriteLogUtil.i("
isIntelligentNoPic=
"
+
i
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Android实用视图动画及工具系列之九(漂亮的图片选择器,高性能防崩溃图片选择工具)
- Android Listview 性能优化
- Android 判断文件的类型
- Android 中 非对称(RSA)加密和对称(AES)加密
- OOP编程(TypeScript类使用详细指南)
- 三星研发班加罗尔| FTE 2019(校园)NIT Rourkela
- 算法题(如何删除链表中的备用节点(详细实现))
- Python使用MySQL-Connector连接MySQL数据库
- Python 2.x和Python 3.x之间的重要区别(代码示例)