Android监听网络状态框架

先来写个最简单的框架吧。最普通的素质三连。1.加权限 2. 注册广播 3.添加回调

就这么简单,让我们开始吧。

看下最终实现的使用:

public class MainActivity extends AppCompatActivity implements NetChangeObserver {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        NetworkManager.getDefault().init(getApplication());
        NetworkManager.getDefault().setListener(this);

    }

    @Override
    public void onConnected(NetType type) {
        Log.i(Constants.TAG,"网络连上了---type="+type);
    }

    @Override
    public void onDisConnected() {
        Log.i(Constants.TAG,"网络断开了");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        NetworkManager.getDefault().logout();
    }
}

日志:

14:22:53.545 1583-1583/com.yirong.netlistener E/NetListener >>>: 网络状态变化了
14:22:53.545 1583-1583/com.yirong.netlistener I/NetListener >>>: 网络连上了
14:22:53.545 1583-1583/com.yirong.netlistener I/NetListener >>>: 网络连上了---type=WIFI

好了,新建一个AndroidLibray开始实现。

权限走一波:

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"></uses-permission>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>

首先,咱们需要一个监听网络状态的工具类

package com.yirong.library.utils;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.yirong.library.NetworkManager;
import com.yirong.library.type.NetType;
/**
 *获取网络状态的工具类
 */
public class NetWorkUtils {
    /**
     * @return 是否有网络
     */
    public static boolean isNetWorkAvailable(){
        ConnectivityManager manager = (ConnectivityManager) NetworkManager.getDefault().getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
        if(manager == null){
            return  false;
        }
        NetworkInfo[] networkInfos = manager.getAllNetworkInfo();
        if(networkInfos != null){
            for(NetworkInfo info:networkInfos){
                if(info.getState() == NetworkInfo.State.CONNECTED){
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * @return 网络类型
     */
    public static NetType getNetworkType(){
        ConnectivityManager manager = (ConnectivityManager) NetworkManager.getDefault().getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
        if(manager == null){
            return  NetType.NONE;
        }
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        if(networkInfo == null){
            return  NetType.NONE;
        }
        int type = networkInfo.getType();
        if(type == ConnectivityManager.TYPE_MOBILE){
            if(networkInfo.getExtraInfo().toLowerCase().equals("cmnet")){
                return NetType.CMNET;
            }else{
                return NetType.CMWAP;
            }
        }else if(type == ConnectivityManager.TYPE_WIFI){
            return NetType.WIFI;
        }
        return NetType.AUTO;
    }

    /**
     * 打开网络设置界面
     * @param context
     * @param requestCode 请求跳转
     */
    public static void openNetSetting(Context context,int requestCode){
        Intent intent = new Intent("/");
        ComponentName cn = new ComponentName("com.android.settings","com.android.settings.WirelessSettings");
        intent.setComponent(cn);
        intent.setAction("android.intent.action.VIEW");
        ((Activity)context).startActivityForResult(intent,requestCode);
    }
}

在lib中创建一个广播,然后再配置文件里注册一下。

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

完善下咱们的receiver:

package com.yirong.library;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.yirong.library.listener.NetChangeObserver;
import com.yirong.library.type.NetType;
import com.yirong.library.utils.Constants;
import com.yirong.library.utils.NetWorkUtils;

public class NetStateReceiver extends BroadcastReceiver {

    NetChangeObserver mNetChangeObserver;
    private NetType type;

    public  NetStateReceiver(){
        this.type = NetType.NONE;
    }

    public void setListener(NetChangeObserver observer){
        mNetChangeObserver = observer;
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent == null || intent.getAction() == null){
            Log.e(Constants.TAG,"广播异常了");
            return;
        }
        if(intent.getAction().equalsIgnoreCase(Constants.ANDROID_NET_CHANGE_ACTION)){
            Log.e(Constants.TAG,"网络状态变化了");
            type = NetWorkUtils.getNetworkType();
            if(NetWorkUtils.isNetWorkAvailable()){
                Log.i(Constants.TAG,"网络连上了");
                mNetChangeObserver.onConnected(type);
            }else{
                Log.i(Constants.TAG,"网络断开了");
                mNetChangeObserver.onDisConnected();
            }
        }
    }
}

监听接口:

package com.yirong.library.listener;

import com.yirong.library.type.NetType;
/**
 * 网络变化监听
 */
public interface NetChangeObserver {
    /**
     * 网络连接成功
     */
    void onConnected(NetType type);
    /**
     * 网络断开
     */
    void onDisConnected();
}

manager:

package com.yirong.library;

import android.app.Application;
import android.content.IntentFilter;

import com.yirong.library.listener.NetChangeObserver;
import com.yirong.library.utils.Constants;

public class NetworkManager {
    private static volatile NetworkManager instance;

    private NetStateReceiver mReceiver;
    private Application mApplication;
    private NetChangeObserver mListener;

    public NetworkManager(){
        mReceiver = new NetStateReceiver();
    }

    public static NetworkManager getDefault(){
        if(instance == null){
            synchronized (NetworkManager.class){
                if(instance == null){
                    instance = new NetworkManager();
                }
            }
        }
        return instance;
    }
    public Application getApplication(){
        if(mApplication == null){
            throw new RuntimeException("NetworkManager.getDefault().init()没有初始化");
        }
        return mApplication;
    }

    public void init(Application application){
        this.mApplication = application;
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Constants.ANDROID_NET_CHANGE_ACTION);
        mApplication.registerReceiver(mReceiver,intentFilter);
    }


    public void logout(){
        getApplication().unregisterReceiver(mReceiver);
    }

    public void setListener(NetChangeObserver listener) {
        mReceiver.setListener(listener);
    }
}

再加上一个枚举类:

/*
 *网络类型
 * */
public enum NetType {
    //有网络,包括Wifi/gprs
    AUTO,
    //wifi
    WIFI,
    //PC/笔记本/PDA
    CMNET,
    //手机端
    CMWAP,
    //没有网络
    NONE
}

OK,一套正常操作,一个最普通网络监听类写完了。接着咱们尝试用注解,让这个小框架看的有档次些。

  • 第一个优化点就是这个枚举类了。

我们都知道枚举类是很消耗内存的。他在编译时会生成一些额外的类和数组,造成运行上的负担。
感兴趣可以了解下:https://www.liaohuqiu.net/cn/posts/android-enum-memory-usage/
而在Android的开发文档里,google建议使用@IntDef/@StringDef注解替代枚举,让我们修改一下枚举类吧。

package com.yirong.library.type;
import android.support.annotation.StringDef;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@StringDef({NetType.AUTO,NetType.WIFI,NetType.CMNET,NetType.CMWAP,NetType.NONE})
public @interface NetType {
    //有网络,包括Wifi/gprs
    public static final String AUTO = "AUTO";
    //wifi
    public static final String WIFI = "WIFI";
    //PC/笔记本/PDA
    public static final String CMNET = "CMNET";
    //手机端
    public static final String CMWAP = "CMWAP";
    //没有网络
    public static final String NONE = "NONE";
}

  • 第二步,我们试图使用注解取代监听接口。这是我们最终想实现的一个效果.
public class MainActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //注册广播
        NetworkManager.getDefault().registerObserver(this);

    }

    //网络监听
    @NetworkListener(type = NetType.AUTO)
    public void netorkListen(@NetType String type){
        switch (type){
            case NetType.AUTO:
                break;
            case NetType.CMNET:
                break;
            case NetType.CMWAP:
                break;
            case NetType.WIFI:
                break;
            case NetType.NONE:
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //注销目标广播
        NetworkManager.getDefault().unRegisterObserver(this);
        //注销所有广播
        NetworkManager.getDefault().unRegisterAllObserver();
    }
}

完善一下自定义注解:

@Target(ElementType.METHOD)//注解目标是方法
@Retention(RetentionPolicy.RUNTIME)//jvm运行时执行
public @interface NetworkListener {//注解名
    @NetType String type() default NetType.AUTO;//参数默认值
}

然后来捋一下思路:

  1. 通过activity的对象,我们可以遍历MainActivity中的所有方法。
  2. 通过注解名判断(NetworkListener ),方法返回类型判断(void),方法参数个数(1)筛选出所有的网络监听方法。
  3. 这时候,当广播监听到网络变化时,根据NetworkUtils获取网络状态,并根据这个参数,使用反射去触发Activity中的方法。

照样是固定三部曲,按照步骤来:

1.先完善我们的注册方法

在这里,我创建了一个方法封装类。比较简单:MethodManager

    /**                                              
     * @param register                               
     */                                              
    public void registerObserver(Object register) {  
        //获取当前Activity or Fragment中所有的网络监听注解方法       
        mMethodList = mNetworkList.get(register);    
        if(mMethodList == null){//说明没注册过了            
            mMethodList = findAnnotationMethod(register)
            mNetworkList.put(register, mMethodList); 
        }                                            
    }                                                
                                                     

这里的mMethodList是方法封装类(MethodManager)的List,用来存储筛选后的MainActivity中方法,也就是网络监听方法。以下是方法的筛选:

 /**                                                                                                
  * @param register                                                                                 
  * @return MethodList 网络监听注解方法数组                                                                   
  */                                                                                                
 private List<MethodManager> findAnnotationMethod(Object register) {                                
     List<MethodManager> methodList = new ArrayList<>();                                            
     Class<?> clazz = register.getClass();                                                          
     Method[] method = clazz.getMethods();                                                          
                                                                                                    
     for (Method m:method){//遍历方法                                                                   
         //找出所有注解方法                                                                                 
         NetworkListener annotation = m.getAnnotation(NetworkListener.class);                       
         if (annotation == null){                                                                   
             continue;                                                                              
         }                                                                                          
         //判断返回类型                                                                                   
         Type genericReturnType = m.getGenericReturnType();                                         
         if(!"void".equals(genericReturnType.toString())){                                          
             throw new RuntimeException(m.getName()+"返回类型必须是void");                                 
         }                                                                                          
                                                                                                    
         //参数校验                                                                                     
         Class<?>[] parameterTypes = m.getParameterTypes();                                         
         Log.i("m,name",m.getParameterTypes().length+"");                                           
         if(parameterTypes.length!= 1){                                                             
             throw new RuntimeException(m.getName()+"返回参数只有一个");                                    
         }                                                                                          
                                                                                                    
         MethodManager methodManager = new MethodManager(parameterTypes[0],annotation.type(),m);    
         methodList.add(methodManager);                                                             
                                                                                                    
     }                                                                                              
     return methodList;                                                                             
 }   
  1. 注销广播就比较简单:
  /**                                                                             
   *注销   
   *@param register                                                              
   */                                                                             
  public void unRegisterObserver(Object register) {                               
      if(!mNetworkList.isEmpty()){//说明有广播被注册过                                     
          mNetworkList.remove(register);                                          
      }                                                                           
      Log.i(Constants.TAG,register.getClass().getName()+"注销成功了");                 
  }                                                                               
                                                                                  
  public void unRegisterAllObserver() {                                           
      if(!mNetworkList.isEmpty()){//说明有广播被注册过                                     
          mNetworkList.clear();                                                   
      }                                                                           
      NetworkManager.getDefault().logout();//注销                                   
  }                                                                               
  1. 接下来是重头戏了!MainActvity中有几个网络监听方法我们已经Get了,接下来就是要在网络发生变化的时候呢,通过反射,没错说的就是你,那个invoke!去触发MainActivty的注解方法。
  @Override                                                                                       
  public void onReceive(Context context, Intent intent) {                                         
      if(intent == null || intent.getAction() == null){                                           
          Log.e(Constants.TAG,"广播异常了");                                                           
          return;                                                                                 
      }                                                                                           
      if(intent.getAction().equalsIgnoreCase(Constants.ANDROID_NET_CHANGE_ACTION)){               
          Log.e(Constants.TAG,"网络状态变化了");                                                         
          type = getNetworkType();                                                                
          post(type); //分发                                                                            
      }                                                                                           
  }                                                                                               

咱们定义一个post方法,去完成这项工作:

/**                                                                                                             
 * @param netType                                                                                               
 */                                                                                                             
private void post(@NetType String netType) {                                                                    
    Set<Object> set = mNetworkList.keySet();                                                                    
    for(Object o: set){                                                                                         
        List<MethodManager> methodManagerList = mNetworkList.get(o);                                            
        for(MethodManager manager:methodManagerList){                                                           
            if(manager.getType().isAssignableFrom(netType.getClass())){//如果注解上的参数和网络状态参数类型相同                      
                switch (manager.getNetType()){                                                                  
                    case NetType.AUTO:                                                                          
                        invoke(manager,o,netType);//反射运行方法                                                      
                        break;                                                                                  
                    case NetType.CMNET:                                                                         
                        if(netType == NetType.CMNET||netType == NetType.NONE ){                                 
                            invoke(manager,o,netType);                                                          
                        }                                                                                       
                        break;                                                                                  
                    case NetType.CMWAP:                                                                         
                        if(netType == NetType.CMWAP||netType == NetType.NONE  ){                                
                            invoke(manager,o,netType);                                                          
                        }                                                                                       
                        break;                                                                                  
                    case NetType.WIFI:                                                                          
                        if(netType == NetType.WIFI ||netType == NetType.NONE ){                                 
                            invoke(manager,o,netType);                                                          
                        }                                                                                       
                        break;                                                                                  
                    case NetType.NONE:                                                                          
                        invoke(manager,o,netType);                                                              
                        break;                                                                                  
                }                                                                                               
            }                                                                                                   
        }                                                                                                       
    }                                                                                                           

这里实现了通过网络监听注解上方的参数,我们可以单独监听某一种网络状态的变化。比如

 //网络监听
    @NetworkListener(type = NetType.WIFI)
    public void netorkListen(@NetType String type){
      ....
    }

上方参数表明,只有在WIFI断开和连接的时候,方法才会做出响应。想要所有提示设置为AUTO就行啦。

OK,做完逻辑以后执行一下:

   /**
     * @param manager 方法管理类
     * @param o 方法所有者(activity/Fragment)
     * @param netType 网络类型参数
     */
    private void invoke(MethodManager manager, Object o, String netType) {
        Method executeMethod = manager.getMethod();
        try {
            executeMethod.invoke(o,netType);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

大功告成!跑一下看看效果!

21:55:36.483 976-976/? E/NetListener >>>: 网络状态变化了
21:55:36.483 976-976/? I/NetListener >>>: WIFI
21:55:36.483 976-976/? I/NetListener >>>: WIFI*

21:55:59.893 976-976/com.yirong.netlistener E/NetListener >>>: 网络状态变化了
21:55:59.893 976-976/com.yirong.netlistener I/NetListener >>>: NONE
21:55:59.893 976-976/com.yirong.netlistener I/NetListener >>>: NONE*

项目地址:https://github.com/CocoYuki/NetListener

  1. 总结
  • 熟练注解使用
  • 反射知识
  • 枚举优化
  • 思想:使用注解实现方法回调,状态筛选

是一个练手的小项目,实际网络请求在7.0时就能通过一个CallBack轻松搞定了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,949评论 6 13
  • 嵌套类和内部类 大部分时候,类被定义成一个独立的程序单元。在某些情况下,也会把一个类放在另一个类的内部定义,这个定...
    凌寒天下独自舞阅读 480评论 0 0
  • ㄆㄟ,呸。例句:简直没脑的智障,我呸!
    艾普羅小姐阅读 154评论 0 0
  • 100天打卡:9/100(9月8日) 1.语音练习100引 2.朗读文章1篇 3.洗澡 小确幸: 了解了一个中介租...
    王恒嘟嘟阅读 189评论 0 0
  • 七七我倒下的时候,烟花正开得无比热闹。 看,烟花!好漂亮啊! 耳边,仿佛又响起她的声音。 当我的身体终于跟大地来了...
    梧桐小生阅读 445评论 0 2