在 Android 7.0 版本中除了提供诸多多窗口支持、活动通知、后台优化、消息传递服务和Vulkan 等新特性和功能外,还对系统和 API 行为做出了各种变更,其中最重要的一点:Android7.0为了进行后台的优化删除了三项隐式广播(网络状态变更广播、拍照广播以及录像广播),以帮助优化内存使用和电量消耗。 此项变更很有必要,比如说网络变化的广播(CONNECTIVITY_CHANGE),当网络发生变化时所有注册了隐式监听网络变化的app都会被启动。删除这些广播可以显著提升设备性能和用户体验。同样地,拍照广播和录视频广播(ACTION_NEW_PICTURE or ACTION_NEW_VIDEO)也会出现上述情况。
该变更导致在Android N平台下即使在Manifest.xml清单文件中注册了 CONNECTIVITY_ACTION广播,在网络发生变化时也不会接收到任何的信息。但是正在前台运行的应用程序如果在主线程中通过Context.registerReceiver()动态注册了CONNECTIVITY_ACTION广播,该应用程序仍然可以接收到该广播。(注:这样开发者就可以根据不同的网络状态加载相应的页面信息了,从而提高用户体验)。
总结:为了避免Android7.0版本接收不到系统网络变化的广播,强烈建议使用动态注册广播的形式来注册广播。
<!-- 访问网络. -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 访问WiFi状态. -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 访问网络状态, 检测网络的可用性. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
工具类见本人博客:安卓项目实战之:网络连接判断工具类
直接Copy过来用即可。
public interface INetEvent {
void onNetChange(int netWorkState);
}
public class NetStateReceiver extends BroadcastReceiver {
private INetEvent mINetEvent= BaseActivity.mINetEvent;
@Override
public void onReceive(Context context, Intent intent) {
// 如果相等的话就说明网络状态发生了变化
if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
//容错机制
if(mINetEvent!=null) {
// 接口回调传过去状态的类型
mINetEvent.onNetChange(NetWorkUtils.getNetWorkState(context));
}
}
}
}
<!-- 监听网络变化的广播 -->
<receiver
android:name=".receiver.NetStateReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
public abstract class BaseActivity extends AppCompatActivity implements INetEvent {
public static INetEvent mINetEvent;
// 网络状态变化的广播接收器
private NetStateReceiver mNetStateReceiver;
// 当前网路连接状态,网络状态发生变化,该值会动态更新
// -1:没有网络 0:移动网络 1:wifi网络
protect int netWorkState;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//初始化网络状态的监听
mINetEvent=this;
// 初始化时检查网络连接
checkNet();
// 动态注册网络状态监听广播
mNetStateReceiver = new NetStateReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mNetStateReceiver , filter);
}
/**
* 初始化时获取当前网络状态
*/
public void checkNet() {
this.netWorkState= NetWorkUtils.getNetWorkState(BaseActivity.this);
}
/**
* 全局检测网络广播的回调 处理网络变化
* @param netWorkState 网络状态 -1:没网络 0:移动网络 1:WiFi网络
*/
public abstract void onNetChanged(int netWorkState);
@Override
public void onNetChange(int netWorkState) {
this.netWorkState= netWorkState;
onNetChanged(netWorkState);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 取消注册
unregisterReceiver(mNetStateReceiver);
// 避免内存泄漏,置空
if(mINetEvent != null){
mINetEvent = null;
}
}
}
public class NetStateActivity extends BaseActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_netstate);
}
@Override
public void onNetChanged(int netWorkState) {
switch (netWorkState) {
case NetWorkUtils.NETWORK_NONE:
//没有网络
break;
case NetWorkUtils.NETWORK_MOBILE:
//移动网络
break;
case NetWorkUtils.NETWORK_WIFI:
//WiFi网络
break;
}
}
}
广播监听只有在网络状态发生变化的情况下才会触发回调,因此当第一次在没网时启动app的时候,该监听并不会回调,所以我们需要在第一次启动app时做手动的判断,上面的处理中我们每次在Activity界面初始化的时候进行了网络判断,并且将获取到的网络值传给了成员变量netWorkState,所以我们可以直接获取该值进行网络状态判断:
// netWorkState就是继承的父类的成员变量
Toast.makeText(this, "当前网络状态:"+netWorkState, Toast.LENGTH_SHORT).show();
如果你觉得上面对网络进行全局监听的方式比较麻烦,那么这里给大家推荐一个安卓全局网络监听的开源库NetStatusBus,使用很简单:
1,添加依赖
compile 'com.sunchen:netstatusbus:0.1.4'
2,在Application 中初始化 NetStatusBus:
// 尽可能早的进行这一步操作, 建议在 Application 中完成初始化操作
NetStatusBus.getInstance().init(this);
3,根据你的生命周期来注册和注销订阅者,(可写在BaseActivity中):
@Override
public void onStart() {
super.onStart();
NetStatusBus.getInstance().register(this);
}
@Override
public void onStop() {
super.onStop();
NetStatusBus.getInstance().unregister(this);
}
4,声明你的订阅方法,在该方法中可以监听到网络状态的变更(可在抽象基类BaseActivity中定义一个非抽象方法,子类选择性重写,实现网路监听):
@NetSubscribe()
public void netChange(NetType netType) {
checkNet(netType);
}
5,然后在子类activity中只需重写checkNet方法即可,如下:
@Override
public void checkNet(NetType netType) {
//netType 会返回当前的网络类型为 NetType.WIFI 还是 NetType.MOBILE 或者NetType.NONE
switch (netType){
case MOBILE:
Toast.makeText(this, "移动网络", Toast.LENGTH_SHORT).show();
break;
case WIFI:
Toast.makeText(this, "wifi网络", Toast.LENGTH_SHORT).show();
break;
case NONE:
Toast.makeText(this, "没有网络", Toast.LENGTH_SHORT).show();
break;
}
}
注意:由于Android 在7.0以后出于性能及安全的考虑对广播做了大量的限制,监听网络连接的广播在7.0以后的系统上也只有动态注册才能生效。 本库出于性能考虑使用 NetworkCallback 类来代替广播实现网络状态变化监听。因此需要将minSdkVersion 升级为21及以上。
另外在注解@NetSubscribe 中还可以指定 mode 用来设置具体的订阅的模式,例如:
// 当 wifi 连接和失去连接时都被调用
@NetSubscribe(mode = Mode.WIFI)
public void wifiChange(NetType netType) {
Log.d(Constrants.LOG_TAG, netType.name());
}
所有支持的mode类型如下:
1,Mode.AUTO
这是默认值,不指明时默认使用该模式,任何网络状态发生变化,该类型订阅者都会被调用。
2,Mode.WIFI
由 WIFI 改变引发的网络状态变化的情况下(wifi连接和断开),该类型订阅者会被调用。
3,Mode.WIFI_CONNECT
仅在 WIFI 成功连接后,该类型订阅者会被调用。
4,Mode.MOBILE
由移动网络改变引发的网络状态变化的情况时(移动网络连接和断开),该类型订阅者会被回调。
5,Mode.MOBILE _CONNECT
仅在移动网络成功连接后,会被回调。
6,Mode.NONE
只有当网络丢失时,该类型订阅者才会被回调。