Android-網(wǎng)絡(luò)監(jiān)控框架NetworkCallback + BroadCastReceiver

????Android網(wǎng)絡(luò)監(jiān)控可以說是一個(gè)老生長談的話題了。但隨著Android API的更新,總有新東西可以聊。我們知道在Android N上移除了部分隱式廣播來達(dá)到防止應(yīng)用被頻繁啟動(dòng)而提升設(shè)備性能的問題。其中就包括:

<receiver android:name=".NetStateReceiver">
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

因此,為了兼容Android N,只能使用動(dòng)態(tài)注冊(cè)的方式來注冊(cè)BroadCastReceiver:

 IntentFilter filter = new IntentFilter();
 filter.addAction(Constants.ACTION_CONNECTIVITY_CHANGE);
 application.registerReceiver(receiver, filter);

????但是巫橄,這不是今天的重點(diǎn),今天主要說的是NetworkCallback,這是Android API 21以后推出的api茵典。有人可能會(huì)說單單使用動(dòng)態(tài)注冊(cè)BroadCastReceiver的方式就可以解決的問題湘换,為什么還要使用這個(gè)不兼容低版本的api?我們要相信android 5.0及以下的手機(jī)逐漸會(huì)退出歷史舞臺(tái)的,新版api總歸是要用起來的统阿。況且NetworkCallback有著更佳的表現(xiàn)彩倚,我們沒有拒絕的理由。

????第一步:實(shí)現(xiàn)一個(gè)NetworkCallback方法:

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class NetworkCallbackImpl extends ConnectivityManager.NetworkCallback {
    private static final String TAG = "NetworkCallbackImpl";
    private Map<Object, List<MethodManager>> networkList = new HashMap<>();

    @Override
    public void onAvailable(Network network) {
        super.onAvailable(network);
        Log.e(TAG,"網(wǎng)絡(luò)連接了");
    }

    @Override
    public void onLost(Network network) {
        super.onLost(network);
        Log.e(TAG,"網(wǎng)絡(luò)斷開了");
    }

    @Override
    public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
        super.onCapabilitiesChanged(network, networkCapabilities);
        if(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)){
            if(networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)){
                Log.e(TAG,"wifi網(wǎng)絡(luò)已連接");
            }else {
                Log.e(TAG,"移動(dòng)網(wǎng)絡(luò)已連接");
            }
        }
    }
}

????通過復(fù)寫以上三個(gè)方法基本就可以滿足我們監(jiān)控網(wǎng)絡(luò)的連接與斷開扶平,wifi網(wǎng)絡(luò)與移動(dòng)網(wǎng)絡(luò)切換的需求了帆离。

????第二步:注冊(cè)NetworkCallbackImpl

NetworkCallbackImpl networkCallback = new NetworkCallbackImpl();
NetworkRequest request = new NetworkRequest.Builder().build();
ConnectivityManager cmgr = (ConnectivityManager) application
        .getSystemService(Context.CONNECTIVITY_SERVICE);
if (cmgr != null) {
    cmgr.registerNetworkCallback(request,networkCallback);
}

????通過以上兩步我們就可以run一把代碼,不出意外结澄,網(wǎng)絡(luò)連接狀態(tài)已經(jīng)可以愉快的打印出來了哥谷。那么問題來了岸夯,我們?nèi)绾卧贏ctivity或者是Fragment中實(shí)時(shí)獲取網(wǎng)絡(luò)狀態(tài)了呢?很簡單们妥,使用接口回調(diào)就可以搞定了囱修。當(dāng)然也可以使用EventBus或者RxBus來進(jìn)行事件傳遞。對(duì)于沒有在項(xiàng)目中使用事件總線的情況王悍,我們不妨來實(shí)現(xiàn)自己的"EventBus"。

  1. 先定義一個(gè)枚舉類型餐曼,用來表示網(wǎng)絡(luò)狀態(tài)
public enum NetType {
    AUTO,//全部狀態(tài)
    NONE,//無網(wǎng)絡(luò)
    WIFI,
    MOBILE
}
  1. 為需要回調(diào)網(wǎng)絡(luò)狀態(tài)的方法定義一個(gè)運(yùn)行時(shí)注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Network {
    NetType netType() default NetType.AUTO;
}
  1. 為Activity中的監(jiān)控方法添加注解
@Network(netType = NetType.AUTO)
public void onNetChanged(NetType netType) {
    switch (netType) {
        case WIFI:
            Log.e(TAG,"AUTO監(jiān)控:WIFI CONNECT");
            break;
        case MOBILE:
            Log.e(TAG,"AUTO監(jiān)控:MOBILE CONNECT");
            break;
        case AUTO:
            Log.e(TAG,"AUTO監(jiān)控:AUTO CONNECT");
            break;
        case NONE:
            Log.e(TAG,"AUTO監(jiān)控:NONE CONNECT");
            break;
        default:
            break;
    }
}

@Network(netType = NetType.WIFI)
public void onWifiChanged(NetType netType){
    switch (netType){
        case WIFI:
            Log.e(TAG,"wifi監(jiān)控:WIFI CONNECT");
            break;
        case NONE:
            Log.e(TAG,"wifi監(jiān)控:NONE CONNECT");
            break;
    }
}

@Network(netType = NetType.MOBILE)
public void onMobileChanged(NetType netType){
    switch (netType){
        case MOBILE:
            Log.e(TAG,"Mobile監(jiān)控:MOBILE CONNECT");
            break;
        case NONE:
            Log.e(TAG,"Mobile監(jiān)控:NONE CONNECT");
            break;
    }
}

????注解和監(jiān)控方法都定義好了压储,我們需要將二者關(guān)聯(lián)起來,也就是我們要在NetworkCallback中為Activity提供注冊(cè)方法源譬。在此之前我們創(chuàng)建一個(gè)注解方法的管理類

4.注解方法管理類

public class MethodManager {

    //被注解方法的參數(shù)類型 NetType netType
    private Class<?> type;

    //需要監(jiān)聽的網(wǎng)絡(luò)類型
    private NetType netType;

    //需要執(zhí)行的方法
    private Method method;

    public MethodManager(Class<?> type, NetType netType, Method method) {
        this.type = type;
        this.netType = netType;
        this.method = method;
    }

    public Class<?> getType() {
        return type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

    public NetType getNetType() {
        return netType;
    }

    public void setNetType(NetType netType) {
        this.netType = netType;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }
}

5.通過對(duì)注解的解析集惋,在NetworkCallback中為Actvitiy提供注冊(cè)與解除注冊(cè)

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class NetworkCallbackImpl extends ConnectivityManager.NetworkCallback {
    private static final String TAG = "NetworkCallbackImpl";
    private Map<Object, List<MethodManager>> networkList = new HashMap<>();

    @Override
    public void onAvailable(Network network) {
        super.onAvailable(network);
        Log.e(TAG,"網(wǎng)絡(luò)連接了");
    }

    @Override
    public void onLost(Network network) {
        super.onLost(network);
        post(NetType.NONE);
    }

    @Override
    public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
        super.onCapabilitiesChanged(network, networkCapabilities);
        if(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)){
            if(networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)){
                post(NetType.WIFI);
            }else {
                post(NetType.MOBILE);
            }
        }
    }

    /**
     * 通知所有注冊(cè)的方法,網(wǎng)絡(luò)發(fā)生了改變
     * @param netType
     */
    private void post(NetType netType) {
        Set<Object> sets = networkList.keySet();
        for (Object observer : sets) {
            List<MethodManager> methodList = networkList.get(observer);
            for (MethodManager method : methodList) {
                if (method.getType().isAssignableFrom(netType.getClass())) {
                    if (method.getNetType() == netType ||
                            netType == NetType.NONE ||
                            method.getNetType() == NetType.AUTO) {
                        invoke(method, observer, netType);
                    }
                }
            }
        }
    }

    private void invoke(MethodManager methodManager, Object observer, NetType netType) {
        try {
            Method execute = methodManager.getMethod();
            execute.invoke(observer, netType);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 注冊(cè)監(jiān)聽
     * @param observer
     */
    public void registerObserver(Object observer) {
        List<MethodManager> methodList = networkList.get(observer);
        if (methodList == null) {
            methodList = getAnnotationMethod(observer);
            networkList.put(observer, methodList);
        }
    }

    /**
     * 遍歷注冊(cè)類中的所有方法踩娘,收集被注解方法的信息
     * @param observer
     * @return
     */
    private List<MethodManager> getAnnotationMethod(Object observer) {
        List<MethodManager> methodList = new ArrayList<>();
        Method[] methods = observer.getClass().getMethods();
        for (Method method : methods) {
            com.px.network.Network network = method.getAnnotation(com.px.network.Network.class);
            if (network == null) {
                continue;
            }
            Log.e(TAG, "NETWORK.....");
            //校驗(yàn)返回值
            Type returnType = method.getGenericReturnType();
            if (!"void".equals(returnType.toString())) {
                throw new RuntimeException(method.getName() + "return type should be null");
            }
            //校驗(yàn)參數(shù)
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length != 1) {
                throw new RuntimeException(method.getName() + "arguments should be one");
            }

            MethodManager methodManager = new MethodManager(parameterTypes[0],
                    network.netType(), method);
            methodList.add(methodManager);
        }
        return methodList;
    }

    public void unRegisterObserver(Object observer) {
        if (!networkList.isEmpty()) {
            networkList.remove(observer);
        }
    }

    public void unRegisterAllObserver() {
        if (!networkList.isEmpty()) {
            networkList.clear();
        }
        NetworkManager.getInstance().getConnectivityManager().unregisterNetworkCallback(this);
        networkList = null;
    }
}

????這樣刮刑,整個(gè)網(wǎng)絡(luò)監(jiān)控框架基本完成,這個(gè)框架中使用了注解养渴,通過反射的方式完成了事件了分發(fā)雷绢,如果你介意使用反射,可以使用接口回調(diào)的方式注冊(cè)事件理卑。完整代碼請(qǐng)查看Demo翘紊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市藐唠,隨后出現(xiàn)的幾起案子帆疟,更是在濱河造成了極大的恐慌,老刑警劉巖宇立,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件踪宠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡妈嘹,警方通過查閱死者的電腦和手機(jī)柳琢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來润脸,“玉大人染厅,你說我怎么就攤上這事〗蚝” “怎么了肖粮?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長尔苦。 經(jīng)常有香客問我涩馆,道長行施,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任魂那,我火速辦了婚禮蛾号,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涯雅。我一直安慰自己鲜结,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布活逆。 她就那樣靜靜地躺著精刷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蔗候。 梳的紋絲不亂的頭發(fā)上怒允,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音锈遥,去河邊找鬼纫事。 笑死,一個(gè)胖子當(dāng)著我的面吹牛所灸,可吹牛的內(nèi)容都是我干的丽惶。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼爬立,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蚊夫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起懦尝,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤知纷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后陵霉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琅轧,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年踊挠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乍桂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡效床,死狀恐怖睹酌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情剩檀,我是刑警寧澤憋沿,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布测蘑,位于F島的核電站锻狗,受9級(jí)特大地震影響齐板,放射性物質(zhì)發(fā)生泄漏景鼠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一壶辜、第九天 我趴在偏房一處隱蔽的房頂上張望悯舟。 院中可真熱鬧,春花似錦砸民、人聲如沸抵怎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽反惕。三九已至,卻和暖如春冗荸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背利耍。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工蚌本, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人隘梨。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓程癌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親轴猎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嵌莉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345