一、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开发实践笔记

Android实现网络监听的更多相关文章

  1. Android 手势水平监听判断

    package com.zihao.ui; import com.zihao.R; import android.os.Bundle; import android.app.Activity; imp ...

  2. Android中如何监听GPS开启和关闭

    转自 chenming 原文 Android中如何监听GPS开启和关闭   摘要: 本文简单总结了如何监听GPS开关的小技巧 有时需要监听GPS的开关(这种需求并不多见).实现的思路是监听代表 GPS ...

  3. Android手机上监听短信的两种方式

    Android手机上监听短信有两种方式: 1. 接受系统的短信广播,操作短信内容. 优点:操作方便,适合简单的短信应用. 缺点:来信会在状态栏显示通知信息. AndroidManifest.xml: ...

  4. iOS 网络监听、判断

    一 网络监听 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary ...

  5. 从零开始学 Web 之 HTML5(三)网络监听,全屏,文件读取,地理定位接口,应用程序缓存

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  6. android的电话监听

    android的电话监听 新建一个项目,结构图如下: PhoneService: package com.demo.tingdianhua; import android.app.Service; i ...

  7. iOS:Reachability网络监听

    iOS利用Reachability确认网络环境3G/WIFI 开发Web等网络应用程序的时候,需要确认网络环境,连接情况等信息.如果没有处理它们,是不会通过Apple的审查的,一般情况下,可以把网络监 ...

  8. java实现网络监听

    Java实现网络监听 import java.net.*; import java.io.*; public class tcpServer { public static void main(Str ...

  9. Android零基础入门第34节:Android中基于监听的事件处理

    原文:Android零基础入门第34节:Android中基于监听的事件处理 上一期我们学习了Android中的事件处理,也详细学习了Android中基于监听的事件处理,同时学会了匿名内部类形式,那么本 ...

随机推荐

  1. uni-app(Vue)中(picker)用联动(关联)选择以至于完成某些功能

    如下图所示,在项目中需求是通过首先选择学生的专业,选好之后在每个专业下面选择对应的学期,每个学期有对应的学费,因此就需要联动选择来实现这一功能. 以下仅展示此功能主要代码: <div class ...

  2. Git基本教程

    git的发展 Git 两周开发 Linus开发,主要是为了管理大量人员维护代码 Git分布式版本控制系统 基本命令 history:查看之前用过的命令 vimtutor git配置 查看配置 git ...

  3. charles抓包修改请求参数发送新的请求

    打开charles -->选择请求右击选择compose---修改参数发送请求

  4. FZU ICPC 2020 寒假训练 5 —— 排序

    P1177 [模板]快速排序 题目描述 利用快速排序算法将读入的 N 个数从小到大排序后输出.快速排序是信息学竞赛的必备算法之一.对于快速排序不是很了解的同学可以自行上网查询相关资料,掌握后独立完成. ...

  5. IntelliJ IDEA竟然出了可以在云端编码的功能?

    前言 自从我用了正版的IntelliJ IDEA后,基本上都是与时俱进,出一个新版本就立马更新,这也能能让我体验到最新最快的功能. 最近在闲逛Jetbrains的官网时,看到了最新的2021.3EAP ...

  6. Django笔记&教程 6-1 表单(Form)介绍

    Django 自学笔记兼学习教程第6章第1节--表单(Form)介绍 点击查看教程总目录 1 介绍 如果网站要让用户输入并提交数据(比如注册登录),则需要用到表单. 单纯的html也能写出表单,格式一 ...

  7. 印象最深的一个bug:sessionStorage缓存在移动端失效

    无bug,不程序:作为程序员的我,不是修bug就是在写bug的路上. 移动端sessionStorage缓存失效是我"印象最深的一个bug"之一,为啥呢,因为这个问题导致我加班到很 ...

  8. MySQL配置参数innodb_flush_log_at_trx_commit

    innodb_flush_log_at_trx_commit 此参数有3个值可设置:0.1.2 0表示每秒刷写一次日志到硬盘,极端情况下MySQL或操作系统挂了最多丢1秒的数据更新 1表示每次事务提交 ...

  9. [bzoj3351]Regions

    这道题有一种较为暴力的做法,对于每个点枚举所有与r2为该属性的询问并加以修改,最坏时间复杂度为o(nq),然而是可过的(97s) 发现只有当r2相同的询问数特别多时才会达到最坏时间复杂度,因此如果删除 ...

  10. generator函数与async/await

    理解async函数就要先理解generator函数,因为async就是Generator函数的语法糖 Generator 函数 Generator 函数是 ES6 提供的一种异步编程解决方案,可以先理 ...