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开发实践笔记
Android实现网络监听的更多相关文章
- Android 手势水平监听判断
package com.zihao.ui; import com.zihao.R; import android.os.Bundle; import android.app.Activity; imp ...
- Android中如何监听GPS开启和关闭
转自 chenming 原文 Android中如何监听GPS开启和关闭 摘要: 本文简单总结了如何监听GPS开关的小技巧 有时需要监听GPS的开关(这种需求并不多见).实现的思路是监听代表 GPS ...
- Android手机上监听短信的两种方式
Android手机上监听短信有两种方式: 1. 接受系统的短信广播,操作短信内容. 优点:操作方便,适合简单的短信应用. 缺点:来信会在状态栏显示通知信息. AndroidManifest.xml: ...
- iOS 网络监听、判断
一 网络监听 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary ...
- 从零开始学 Web 之 HTML5(三)网络监听,全屏,文件读取,地理定位接口,应用程序缓存
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...
- android的电话监听
android的电话监听 新建一个项目,结构图如下: PhoneService: package com.demo.tingdianhua; import android.app.Service; i ...
- iOS:Reachability网络监听
iOS利用Reachability确认网络环境3G/WIFI 开发Web等网络应用程序的时候,需要确认网络环境,连接情况等信息.如果没有处理它们,是不会通过Apple的审查的,一般情况下,可以把网络监 ...
- java实现网络监听
Java实现网络监听 import java.net.*; import java.io.*; public class tcpServer { public static void main(Str ...
- Android零基础入门第34节:Android中基于监听的事件处理
原文:Android零基础入门第34节:Android中基于监听的事件处理 上一期我们学习了Android中的事件处理,也详细学习了Android中基于监听的事件处理,同时学会了匿名内部类形式,那么本 ...
随机推荐
- jmeter 数据库压力测试之MySql
1.首先下载合适的数据库驱动:https://mvnrepository.com/artifact/mysql/mysql-connector-java 2.创建testplan,并添加jar包 3. ...
- Java8新特性之方法引用&Stream流
Java8新特性 方法引用 前言 什么是函数式接口 只包含一个抽象方法的接口,称为函数式接口. 可以通过 Lambda 表达式来创建该接口的对象.(若 Lambda 表达式抛出一个受检异常(即:非运行 ...
- Linux ns 6. Network Namespace 详解
文章目录 1. 简介 1.1 Docker Network 桥接模式配置 2. 代码解析 2.1 copy_net_ns() 2.2 pernet_list 2.2.1 loopback_net_op ...
- CSS基础-行快属性,hover
CSS基础 1.行快属性 在css中有很多标签,分为行内标签,块标签,标签行内块标签,他们有着不同的属性. 块标签 div,ul,li,ol,h1~h6,p 可 ...
- 网页视频不能自动播放?HTML5 video报错Uncaught (in promise) DOMException解决方法
话说发哥四年前写了一个网页,如上图效果,实际网址http://pano.z01.com ,话说做好时是正常的,突然某一天,客户说你这个网站动画不见了,这是什么原因? 结果检查脚本一切正常. 其实也不是 ...
- 菜鸡的Java笔记
1.注释 在JAVA中对于注释有三种: 单行注释:// 多行注释:/*--*/ 文档注释:/**--*/ 2.关键字和标识符 在程序中描述类名称,方法名称,变量等概念就需要使用标识符来定义.而在JAV ...
- Python 字符串索引、切片、修改
字符串索引.切片.修改1.字符串操作(切片.修改)应用场景 a.爬虫截取网址数据 b.数据分析,语言处理(分词) c.电信号码升级 0452 8869504 ...
- 带allow-create的el-select限制长度
需求:给el-select添加新增字段长度限制且新增内容不能为空 1.首先给el-select绑定一个id(例如:selectSku),这个id会传到组件里面,绑定在那个input上面, <el ...
- [bzoj3038]上帝造题的7分钟2
考虑每一个位置最多开6次左右就会变成1,然后操作就没有意义了,因此对线段树维护区间和和一个标记,表示是否全部都是1,然后对于修改,如果区间标记不是1就暴力下去,是1就不用操作,复杂度为$o(6nlog ...
- 如何利用 JuiceFS 的性能工具做文件系统分析和调优
JuiceFS 是一款面向云原生环境设计的高性能 POSIX 文件系统,在 AGPL v3.0 开源协议下发布.作为一个云上的分布式文件系统,任何存入 JuiceFS 的数据都会按照一定规则拆分成数据 ...