須知:Android7.0起網絡變化監聽隐式廣播被關閉
在 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版本接收不到系統網絡變化的廣播,強烈建議使用動态注冊廣播的形式來注冊廣播。
1,添權重限
<!-- 通路網絡. -->
<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" />
2,編寫網絡監聽工具類
工具類見本人部落格:安卓項目實戰之:網絡連接配接判斷工具類
直接Copy過來用即可。
3,建立一個接口來接收狀态變化的回調
public interface INetEvent {
void onNetChange(int netWorkState);
}
4,建立一個BroadcastReceiver,監聽網絡狀态變化
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));
}
}
}
}
5,在清單檔案中配置 (該步驟取消,為适配7.0已在BaseActivity中動态注冊)
<!-- 監聽網絡變化的廣播 -->
<receiver
android:name=".receiver.NetStateReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
6,在基類BaseActivity中實作INetEvent接口
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;
}
}
}
7,具體Activity的實作
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,添加依賴
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
隻有當網絡丢失時,該類型訂閱者才會被回調。