Android实现网络监听

一、Android Wifi常用广播

网络开发中主体会使用到的action:

ConnectivityManager.CONNECTIVITY_ACTION
WifiManager.WIFI_STATE_CHANGED_ACTION
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION
WifiManager.NETWORK_IDS_CHANGED_ACTION
WifiManager.SUPPLICANT_STATE_CHANGED_ACTION
WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION
WifiManager.LINK_CONFIGURATION_CHANGED_ACTION
WifiManager.NETWORK_STATE_CHANGED_ACTION
WifiManager.RSSI_CHANGED_ACTION

1、ConnectivityManager.CONNECTIVITY_ACTION:
      网络连接发生了变化的广播, 通常是默认的连接类型已经建立连接或者已经失去连接会触发的广播; 监听到这个广播之后, 可以从intent中获取字段ConnectivityManager.EXTRA_NO_CONNECTIVITY, 如果返回true, 代表当前连接断开. 否则,连接成功.同时可以从intent中取出字段 ConnectivityManager.EXTRA_NETWORK_INFO, 返回NetWorkInfo, 通过此对象, 你会获取当前连接的一些更为具体的信息. 部分代码如下:

    // 是否无连接.
    public static boolean isNoConnectivity(Intent intent) {
        return intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
    }
    
    // 获取当前网络信息. 
    public static NetworkInfo getExtraNetworkInfo(Intent intent) {
        return intent.getPacelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
    }
    
    // 获取当前网络状态. 
    public static NetworkInfo.State getNetState(NetworkInfo info) {
        return info == null ? null : info.getState();
    }
    
    // 获取当前网络类型. 
    public static int getNetState(NetworkInfo info) {
        return info == null ? -1 : info.getType();
    }

2、WifiManager.WIFI_STATE_CHANGED_ACTION:
      WiFi模块硬件状态改变的广播, 对于肉眼而言, 看到的直观表征有, WiFi开启, WiFi关闭; 而在实际的过程中, WIFI 从开启到关闭, 或是从关闭到开启, 需要经历三个状态, 以开启WIFI为例, 其要经过的状态分别为: 已关闭, 开启中, 已开启. 关闭WIFI则相反, 分为为: 已开启, 关闭中, 关闭. 接收到这个广播后, 你可以从intent中取出当前WiFi硬件的变化状态, 可以使用 int 值来区别; 这个key是: EXTRA_WIFI_STATE, 可能得到的值为:0, 1, 2, 3, 4; 当然除了这种获取方式, 也可以通过WiFiManager对象getWifiState() 获取这个值. 也可以从 intent 中取出另外一个值, 表示之前WiFi模块的状态, 是不是很爽? 那么, 对应的key, 就是: EXTRA_PREVIOUS_WIFI_STATE;

    // 通过 intent 获取当前WIFI状态. 
    public static int getWifiStateByIntent(Intent intent) {
        return intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
    }
    
    // 通过 WifiManager 获取当前WIFI状态. 
    public static int getWifiStateByWifiManager(WifiManager manager) {
        return manager == null ? WifiManager.WIFI_STATE_UNKNOWN : manager.getState();
    }
    
    // 获取WIFI前一时刻状态. 
    public static int getWifiPreviousState(Intent intent) {
        return intent.getIntExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
    }

其中:
 0 --> WiFiManager.WIFI_STATE_DISABLING, 表示 WiFi 正关闭的瞬间状态;
 1 --> WifiManager.WIFI_STATE_DISABLED, 表示 WiFi 模块已经完全关闭的状态; 
 2 --> WifiManager.WIFI_STATE_ENABLING, 表示 WiFi 模块正在打开中瞬间的状态; 
 3 --> WiFiManager.WIFI_STATE_ENABLED, 表示 WiFi 模块已经完全开启的状态;
 4 --> WiFiManager.WIFI_STATE_UNKNOWN, 表示 WiFi 处于一种未知状态; 通常是在开启或关闭WiFi的过程中出现不可预知的错误, 通常是底层状态机可能跑的出现故障了, 会到这种情况, 与底层控制相关; 

3、WifiManager.SCAN_RESULTS_AVAILABLE_ACTION:
      扫描到一个热点, 并且此热点达可用状态 会触发此广播; 此时, 你可以通过wifiManager.getScanResult() 来取出当前所扫描到的ScanResult; 同时, 你可以从intent中取出一个boolean值; 如果此值为true, 代表着扫描热点已完全成功; 为false, 代表此次扫描不成功, ScanResult距离上次扫描并未得到更新;

    // 获取 ScanResult 列表: 
    public static List<ScanResult> getScanResultForWifi(WifiManager manager) {
        return manager == null ? null : manager.getScanResult();
    }

    // result 是否更新: 
    public static boolean isResultUpdated(Intent intent) {
        return intent != null && intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
    }

4、WifiManager.NETWORK_IDS_CHANGED_ACTION:
      在网络配置, 保存, 添加, 连接, 断开, 忘记的操作过后, 均会对 WIFI 热点配置形成影响, 在shell下, 如果有root权限, 可以在执行上述动作前后, 分别浏览 /data/misc/wifi/wpa_supplicant.conf 应该是有本质的变化, 此时会收到此广播.具体的执行指令为:

    adb shell 
    $ cat /data/misc/wifi/wpa_supplicant.conf

5、WifiManager.SUPPLICANT_STATE_CHANGED_ACTION:
      建立连接的热点正在发生变化. 象征变化的相关类为: SupplicantState, 你可以在接收到此广播时, 观察到已经建立连接的热点的整个连接过程, 包含可能会出现连接错误的错误码. 相关代码为:

    // 获取当前网络新状态. 
    public static SupplicantState getCurrentNetworkState(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
    }
    
    // 获取当前网络连接状态码. 
    public static int getCurrentNetworkCode(Intent intent) {
        return int netConnectErrorCode = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, 0);
    }
    
    // 当前网络是否连接失败
    public static boolean isCurrentNetworkConnectFailed(intent intent) {
        return WifiManager.ERROR_AUTHENTICATING == getCurrentNetworkCode(intent);
    }

6、WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION:
      官方的注释是这么说的, 广播已配置的网络发生变化, 可由添加, 修改, 删除网络的触发. 当从intent中取出key值为EXTRA_MULTIPLE_NETWORKS_CHANGED, 其值为true时, 那么字段EXTRA_WIFI_CONFIGURATION中取出来的配置已经过时, 不是最新配置了, 具体的代码为:

    // 是否多重网络发生变化. 
    public static boolean isMultipleNetworkChanged(Intent intent) {
        return intent.getBooleanExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
    }
    
    // 获取当前最新网络配置: 
    public static WifiConfiguration getCurWifiConfig(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_WIFI_CONFIGURATION);
    }

7、WifiManager.LINK_CONFIGURATION_CHANGED_ACTION:
      WIFI连接配置发生改变的广播. 此时, 网路连接功能封装LinkProperties和NetworkCapabilities可能发生变化.

    // 获取 LinkProperties 
    public static LinkProperties getLinkProperties(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_LINK_PROPERTIES);
    }
    
    // 获取 NetworkCapabilities
    public static NetworkCapabilities getNetworkCapabilities(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_CAPABILITIES);
    }

8、WifiManager.NETWORK_STATE_CHANGED_ACTION:
      WIFI连接状态发生改变的广播. 可以从intent中取得NetworkInfo, 此时NetworkInfo中提供了连接的新状态, 如果连接成功, 可以获取当前连接网络的BSSID, 和WifiInfo. 相关代码:

    // 获取当前网络
    public static NetworkInfo getCurrentNetworkInfo(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
    }
    
    // 获取当前网路状态. 
    public static NetworkInfo.State getCurrentNetworkState(NetworkInfo info) {
        return info != null ? info.getState() : null;
    }
    
    // 获取当前网路BSSID.
    public static String getCurrentNetworkBssid(Intent intent) {
        return intent.getStringExtra(WifiManager.EXTRA_BSSID);
    }
    
    // 获取当前网路的WifiInfo. wifi 连接成功有效.
    public static WifiInfo getCurrentWifiInfo(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
    }

9、WifiManager.RSSI_CHANGED_ACTION:
      WIFI热点信号强度发生变化的广播. 可以获取当前变化热点的最新的信号强度.

    // 获取当前热点最新的信号强度
    public static int getCurrentNetworkRssi(Intent intent) {
        return intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -1000);
    }

二、Android即时网络监听——BroadcastReceiver
      通常我们在进行网络请求之前,会先进行网络状态判断,再决定是否进行网络请求,常用方法如下:

/**
* 判断网络是否可用
*
* @return true/false
*/
@SuppressLint("MissingPermission")
public static boolean isNetworkAvailable() {
    ConnectivityManager connMgr = (ConnectivityManager) NetworkListener.getInstance().getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
    if (connMgr == null) {
        return false;
    }
    NetworkInfo[] infos = connMgr.getAllNetworkInfo();
    if (infos != null) {
        for (NetworkInfo info : infos) {
            if (info.getState() == NetworkInfo.State.CONNECTED) {
                return true;
            }
        }
    }
    return false;
}


/**
* 获取当前网络类型
*
* @return NetType
*/
@SuppressLint("MissingPermission")
public static NetType getNetType() {
    ConnectivityManager connMgr = (ConnectivityManager) NetworkListener.getInstance().getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
    if (connMgr == null) {
        return NetType.NONE;
    }
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    if (networkInfo == null) {
        return NetType.NONE;
    }
    int nType = networkInfo.getType();
    if (nType == ConnectivityManager.TYPE_MOBILE) {
        if ("cmnet".equals(networkInfo.getExtraInfo().toLowerCase())) {
            return NetType.CMNET;
        } else {
            return NetType.CMWAP;
        }
    } else if (nType == ConnectivityManager.TYPE_WIFI) {
        return NetType.WIFI;
    }
    return NetType.NONE;
}

      但这种做法无法做到即时网络监听,例如在播放视频或下载文件过程中,网络状态发生变化,我们无法做到即时处理,解决方式有2种:
      (1)使用广播方式
      (2)使用ConnectivityManager.NetworkCallback方式
      本次我们先使用广播监听的方式进行网络变化监听。

      我们知道,网络状态发生变化的时候,系统会发出android.net.CONNECTIVITY_CHANGE广播,所以我们可以用监听此广播的方式来判断网络连接:

1、权限申请

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

2、创建常量

//网络连接改变广播
public static final String ANDROID_NET_CHANGE_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";

3、定义枚举类,表示网络状态:

public enum NetType {
    //任意网络
    AUTO,
    //WIFI
    WIFI,
    CMNET,
    //手机上网
    CMWAP,
    //无网络
    NONE
}

4、定义网络监听接口:

public interface NetChangeListener {
    /**
     * 已连接
     * @param netType NetType
     */
    void onConnect(NetType netType);

    /**
     * 连接断开
     */
    void onDisConnect();
}

5、创建广播接收者:

public class NetworkStateReceiver extends BroadcastReceiver {
    private static final String TAG = "NetworkStateReceiver";
    private NetType netType;//网络类型
    private NetChangeListener listener;

    public NetworkStateReceiver() {
        //初始化网络连接状态
        this.netType = NetType.NONE;
    }

    public void setListener(NetChangeListener listener) {
        this.listener = listener;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent == null || intent.getAction() == null) {
            Log.e(TAG, "onReceive: 异常");
            return;
        }
        if (intent.getAction().equals(Constants.ANDROID_NET_CHANGE_ACTION)) {
            Log.d(TAG, "onReceive: 网络发生变化");
            //获取当前联网的网络类型
            netType = NetworkUtils.getNetType();
            if (NetworkUtils.isNetworkAvailable()) {
                Log.d(TAG, "onReceive: 网络连接成功");
                if (listener != null) {
                    listener.onConnect(netType);
                }
            } else {
                Log.e(TAG, "onReceive: 网络连接失败");
                if (listener != null) {
                    listener.onDisConnect();
                }
            }
        }
    }
}

6、定义NetworkListener类(单例)用于初始化监听:

public class NetworkListener {
    private Context context;
    private NetworkStateReceiver receiver;

    /**
     * 私有化构造方法
     */
    private NetworkListener() {
        receiver = new NetworkStateReceiver();
    }

    private static final SingletonTemplate<NetworkListener> INSTANCE = new SingletonTemplate<NetworkListener>() {
        @Override
        protected NetworkListener create() {
            return new NetworkListener();
        }
    };

    public static NetworkListener getInstance() {
        return INSTANCE.get();
    }

    public Context getContext() {
        return context;
    }

    public void setListener(NetChangeListener listener) {
        receiver.setListener(listener);
    }

    /**
     * 初始化
     *
     * @param context context
     */
    @SuppressLint("MissingPermission")
    public void init(Context context) {
        this.context = context;
        IntentFilter filter = new IntentFilter();
        filter.addAction(Constants.ANDROID_NET_CHANGE_ACTION);
        context.registerReceiver(receiver, filter);
    }
}

7、使用:
Application中初始化:

NetworkListener.getInstance().init(this);

在需要监听的Activity中实现NetChangeListener接口,并在onCreate()中初始化NetworkListener,如下:

public class MainActivity extends AppCompatActivity implements NetChangeListener {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        NetworkListener.getInstance().setListener(this);
    }

    @Override
    public void onConnect(NetType netType) {
        Log.d(TAG, "onConnect: 网络连接成功:状态_" + netType.name());
    }

    @Override
    public void onDisConnect() {
        Log.e(TAG, "onDisConnect: 网络连接断开");
    }
}

通过以上代码,我们可以实现在APP运行过程中实时监听网络连接状态,此方式使用广播监听结合接口回调实现。

三、Android即时网络监听——ConnectivityManager.NetworkCallback

1、新建NetworkCallbackImpl类继承ConnectivityManager.NetworkCallback,并重写onAvailable、onLost、onCapabilitiesChanged三个方法:

@Override
public void onAvailable(Network network) {
    super.onAvailable(network);
    Log.d(TAG, "onAvailable: 网络已连接");
}

@Override
public void onLost(Network network) {
    super.onLost(network);
    Log.e(TAG, "onLost: 网络已断开");
}

@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.d(TAG, "onCapabilitiesChanged: 网络类型为wifi");
            post(NetType.WIFI);
        } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
            Log.d(TAG, "onCapabilitiesChanged: 蜂窝网络");
            post(NetType.CMWAP);
        } else {
            Log.d(TAG, "onCapabilitiesChanged: 其他网络");
            post(NetType.AUTO);
        }
    }
}

2、注册NetworkCallbackImpl:

NetworkCallbackImpl networkCallback = new NetworkCallbackImpl();
NetworkRequest.Builder builder = new NetworkRequest.Builder();
NetworkRequest request = builder.build();
ConnectivityManager connMgr = (ConnectivityManager) NetworkListener.getInstance().getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
if (connMgr != null) {
    connMgr.registerNetworkCallback(request, networkCallback);
}

做完以上两步之后,运行代码,不出意外的话,网络状态改变就可以正常检测出来了。


参考博客:
【1】Android WIFI开发之广播监听
【2】Android-WiFi开发之 WiFi广播监听(格式化版)
【3】Android即时网络监听(一)-BroadcastReceiver
【4】Android即时网络监听(二)-ConnectivityManager.NetworkCallback
【5】Android WiFi P2P开发实践笔记

posted on 2021-06-04 23:15  渴望飞翔的xian鱼  阅读(1464)  评论(0编辑  收藏  举报

导航