网络

Android监听网络状态框架

2019-03-02  本文已影响127人  在一颗大大大榕树下

先来写个最简单的框架吧。最普通的素质三连。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轻松搞定了。

上一篇 下一篇

猜你喜欢

热点阅读