监听Android设备网络变化 - 亲测在华为Emui8.0 以及 Android原生5.1、7.1.2有效
1、本文章基于:监听Android设备网络变化 -
2、其他的优秀开源库:GHdeng/NetMonitor: 使用广播监听网络变化更新UI , 这个库也很好,也推荐伙伴们去使用哈。
3、其他的优秀文章:[23]—— 10分钟让你实现在APP中对网络状态变化进行全局提示 -
原理分析与源码介绍
0x01 目标
在实际开发中,我们不可避免地需要对请求错误进行处理,通常情况下,我们会这样去处理错误请求:
- 没有网络的情况下,提示用户网络连接不可用,引导用户打开网络或重新刷新等
- 有网络的情况下,则是客户端或服务端的错误,给用户相应的提示
如果针对第一种情况,我们需要在网络恢复的时候重新刷新数据或进行其他操作,又应该如何实现呢?以下就是我们的目标:
(1)、 监听Android设备网络状态
(2)、 在网络状态发生改变时,做出相应操作
在示例中,我们在网络状态发生变化时,显示当前网络变化的类型。
0x02 思路
在Android系统在网络变化的情况下,会发出 action 为 ConnectivityManager.CONNECTIVITY_ACTION
的系统广播,我们只需要注册 BroadcastReceiver
去监听该广播即可监听设备的网络变化情况。
那么,注册 BroadcastReceiver
是静态注册呢,还是动态注册呢?
- 静态注册:通常来讲,退出应用后,该应用仍然能够接收到相应的广播
- 动态注册:随着所在Context或应用被销毁后,不会收到相应的广播
注意:针对静态注册,这里是用“通常来讲”来修饰的,也就是说,存在特殊情况,即:存在即使使用静态注册,也不会收到相应的广播的情况:
Android3.1之后,系统为了加强了安全性控制,应用程序安装后或是(设置)应用管理中被强制关闭后处于stopped状态,在这种状态下接收不到任何广播,除非广播带有
FLAG_INCLUDE_STOPPED_PACKAGES
标志,而默认所有系统广播都是FLAG_EXCLUDE_STOPPED_PACKAGES
的,所以就没法通过系统广播自启动了。 这其中就包括ConnectivityManager.CONNECTIVITY_ACTION
。
关于这一块的内容,不是本篇重点,欲了解详情,请移步Android应用为何开机自启动、自启动失败原因
另外,Android 7.0 移除了三个隐式广播(Android 7.0 行为变更),其中就包括 ConnectivityManager.CONNECTIVITY_ACTION
, 这意味着通过静态注册 BroadcastReceiver
来监听该广播的方式在 targetSdkVersion >= 24
版本上不再生效,如何解决这一问题请移步 Android 7.0 网络变化监听。
所以,这里采取动态注册 BroadcastReceiver
的方式。那么,应该在哪里动态注册呢?这里有两种思路:
-
- 定义
BroadcastReceiver
监听网络状态,并提供回调接口NetStateChangeObserver
用以回调网络状态的变化 - 抽象出
BaseActivity
,提供注册/取消注册BroadcastReceiver
的方法,并实现NetStateChangeObserver
- 需要监听网络状态的
Activity
调用BaseActivity
提供的方法即可
- 定义
-
- 定义
BroadcastReceiver
监听网络状态,并提供回调接口NetStateChangeObserver
用以回调网络状态的变化,并在BroadcastReceiver
中维护NetStateChangeObserver
列表,当网络发生变化则通知这些 Observer ,实现回调。 - 在
Application
中注册/取消注册BroadcastReceiver
- 抽象
BaseActivity
,提供注册/取消注册NetStateChangeObserver
观察者的方法, 并实现NetStateChangeObserver
- 需要监听网络状态的
Activity
调用BaseActivity
提供的方法即可
- 定义
上面的两种思路,比较重要的区别在于,第1种是在 Activity
中注册 BroadcastReceiver
, 第2种是在 Application
中注册 BroadcastReceiver
。前者需要多次注册 BroadcastReceiver
而后者只注册一次,所以在这里选择第2种思路。
0x03 实现 - 亲测在华为Emui8.0 以及 Android原生7.1.2有效
添加权限
定义网络类型
public enum NetworkType { NETWORK_WIFI("WiFi"), NETWORK_4G("4G"), NETWORK_3G("3G"), NETWORK_2G("2G"), NETWORK_UNKNOWN("Unknown"), NETWORK_NO("No network"); private String desc; NetworkType(String desc) { this.desc = desc; } @Override public String toString() { return desc; }}
定义观察者
/** * 网络状态变化观察者 */public interface NetStateChangeObserver { void onNetDisconnected(); void onNetConnected(NetworkType networkType);}
实现 BroadcastReceiver
/** * 监听网络状态变化的BroadcastReceiver */public class NetStateChangeReceiver extends BroadcastReceiver { private static class InstanceHolder { private static final NetStateChangeReceiver INSTANCE = new NetStateChangeReceiver(); } private List mObservers = new ArrayList<>(); public NetStateChangeReceiver() { } @Override public void onReceive(Context context, Intent intent) { if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { NetworkType networkType = NetworkUtils.getNetworkType(context); notifyObservers(networkType); } } /** * 注册网络监听 */ public static void registerReceiver(@NonNull Context context) { IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); context.registerReceiver(InstanceHolder.INSTANCE, intentFilter); } /** * 取消网络监听 */ public static void unregisterReceiver(@NonNull Context context) { context.unregisterReceiver(InstanceHolder.INSTANCE); } /** * 注册网络变化Observer */ public static void registerObserver(NetStateChangeObserver observer) { if (observer == null) return; if (!InstanceHolder.INSTANCE.mObservers.contains(observer)) { InstanceHolder.INSTANCE.mObservers.add(observer); } } /** * 取消网络变化Observer的注册 */ public static void unregisterObserver(NetStateChangeObserver observer) { if (observer == null) return; if (InstanceHolder.INSTANCE.mObservers == null) return; InstanceHolder.INSTANCE.mObservers.remove(observer); } /** * 通知所有的Observer网络状态变化 */ private void notifyObservers(NetworkType networkType) { if (networkType == NetworkType.NETWORK_NO) { for(NetStateChangeObserver observer : mObservers) { observer.onNetDisconnected(); } } else { for(NetStateChangeObserver observer : mObservers) { observer.onNetConnected(networkType); } } }}
Application 注册 BroadcastReceiver
public class AppContext extends Application{ @Override public void onCreate() { super.onCreate(); // 注册BroadcastReceiver NetStateChangeReceiver.registerReceiver(this); } @Override public void onTerminate() { super.onTerminate(); // 取消BroadcastReceiver注册 NetStateChangeReceiver.unregisterReceiver(this); }}
BaseActivity 抽取
public class BaseActivity extends AppCompatActivity implements NetStateChangeObserver { @Override protected void onResume() { super.onResume(); if (needRegisterNetworkChangeObserver()) { NetStateChangeReceiver.registerObserver(this); } } @Override protected void onStop() { super.onStop(); if (needRegisterNetworkChangeObserver()) { NetStateChangeReceiver.unregisterObserver(this); } } /** * 是否需要注册网络变化的Observer,如果不需要监听网络变化,则返回false;否则返回true.默认返回false */ protected boolean needRegisterNetworkChangeObserver() { return false; } @Override public void onNetDisconnected() { } @Override public void onNetConnected(NetworkType networkType) { }}
需要实现网络监听的 Activity 只需要复写 needRegisterNetworkChangeObserver
并返回 true ,并复写相关回调函数即可。
错误做法
-
网络最新的一片文章便是“觉得低于Android N(24)的就使用BroadcastReceiver,高于Android N(24)
就是用JobScheduler”(理由:Android 7.0 为了后台优化,推荐使用 JobScheduler 代替 BroadcastReceiver 来监听网络变化。),
比如下面这种写法,下面这种写法在华为M5上不起作用(M5 Emui8.0基于Android 8.0):
@SuppressLint("NewApi") private void networkCallback() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { mNetReceiver = new NetReceiver(); IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(mNetReceiver, intentFilter); return; } //亲测在华为Emui8.0 以及 Android原生7.1.2无效 //https://www.jianshu.com/p/83da8f88ce9c final ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivityManager == null) { return; } connectivityManager.requestNetwork(new NetworkRequest.Builder().build(), new ConnectivityManager.NetworkCallback() { @Override public void onLost(Network network) { super.onLost(network); ///网络不可用的情况下的方法 } @Override public void onAvailable(Network network) { super.onAvailable(network); ///网络可用的情况下的方法 loadWebUrl(); } }); } public class NetReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); if (manager == null) { return; } NetworkInfo mobileInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (wifiInfo != null && (wifiInfo.isConnected() || mobileInfo.isConnected())) { loadWebUrl(); } } }
更多相关文章
- [置顶] 整个网络可能最完善的 Android(安卓)自定义键盘 问题汇总
- Android的事件处理机制之基于监听的事件处理
- 在android下出现乱码的问题(在网络中传输和获取数据时)
- Android通过代码打开和关闭网络连接
- [原] Android监听网络的变化
- API接口JWT方式的Token认证(下),客户端(Android)的实现
- Android视频播放器实现小窗口和全屏状态切换
- Android判断设备网络连接状态及判断连接方式的方法
- Android直播开发之旅(16):使用FFmpeg保存网络流到本地文件