天天看點

安卓項目實戰之:安卓7.0優化下的全局網絡連接配接狀态監聽

須知: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

隻有當網絡丢失時,該類型訂閱者才會被回調。