美文网首页网络
Android监听网络状态框架

Android监听网络状态框架

作者: 在一颗大大大榕树下 | 来源:发表于2019-03-02 21:58 被阅读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轻松搞定了。

相关文章

  • Android监听网络状态框架

    先来写个最简单的框架吧。最普通的素质三连。1.加权限 2. 注册广播 3.添加回调 就这么简单,让我们开始吧。 看...

  • Android监听网络状态

    几乎所有的安卓应用都会用到网络,除去极少数纯单机的应用。而在应用使用网络的过程中,必然会收到网络状态的影响。特别是...

  • Android网络状态监听

    1.添加权限 2.获取ConnectivityManager 3监听网络状态 网络监听广播

  • 网络状态

    监听网络连接状态 使用AFNetWorking框架中的AFNetworkReachabilityManager实现...

  • Android 检测网络类型及是否能用

    Android 中网络连接检测和使用ping检测网络是否可访问Android:检测网络状态&监听网络变化 获得Co...

  • iOS开发之网络监听

    iOS监听网络方式缺点通过状态栏判断一旦状态栏隐藏就会失效AFNetworking中的网络监听模块必须导入该框架,...

  • (笔记)Android监听网络状态

    在Android中对于网络状态的监听,这里做最简单的一种。直接上步骤:1、首先在Activity或者Fragmen...

  • Android广播监听网络状态

    1.先说一下广播,广播分为两种:静态广播和动态广播。静态广播即是静态注册的,而动态广播是代码动态注册。静态注册是通...

  • Android网络状态变化监听

    Android网络状态监听实现 功能分析 背景介绍 为了给用户一个好的使用体验,尤其是一些视频、图片类型的app,...

  • Android监听网络连接状态变化

    对于Android监听网络连接状态变化,网上太多这类资料了,而且对于网络连接状态和网络开关状态很容易混淆不清。 对...

网友评论

    本文标题:Android监听网络状态框架

    本文链接:https://www.haomeiwen.com/subject/mtbhuqtx.html