WiFi相关操作
前段时间学习了Android中WiFi相关操作,一直没来得及写。乘着过年只能在家待写一写。关于WiFi扫描、WiFi基本信息、WiFi连接等操作。
1、所需权限
必须在AndroidManifest中申请以下3种权限,缺一不可。缺一个都无法扫描或者得到扫描结果。详细权限需求说明查看官方API。
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>最好再动态监测一下有没有相应的权限,如果没有就动态申请。这里不再累述。
2、WifiManager
2.1、获取WifiManager
对WiFi的操作需要通过WifiManager对象来完成,getSystemService()获取WifiManager对象:
WifiManager mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);2.2、WiFiManager常用方法
WifiManager封装了大量方便的API,方便我们操作WiFi
//添加一个config描述的WIFI网络,默认情况下,这个WIFI网络是DISABLE状态的。
addNetwork(WifiConfiguration config)
//计算信号的等级
calculateSignalLevel(int rssi , int numLevels)
//对比网络A和网络B的信号强度
compareSignalLevel(int rssiA, int rssiB)
//创建一个WIFI 锁,锁定当前的WIFI连接
createWifiLock(int lockType, String tag)
//让一个网络连接失效
disableNetwork(int netId)
//断开当前的WIFI连接
disconnect()
//连接netId所指的WIFI网络,并是其他的网络都被禁用
enableNetwork(int netId, Boolean disableOthers)
//获取网络连接的状态
getConfiguredNetworks()
//获取当前连接的信息
getConnectionInfo()
//获取DHCP 的信息
getDhcpInfo()
//获取扫描测试的结果
getScanResulats()
//获取当前WIFI设备的状态
getWifiState()
//判断WIFI设备是否打开
isWifiEnabled()
//ping操作,和PC的ping操作相同作用
pingSupplicant()
//重新连接WIFI网络,即使该网络是已经被连接上的
ressociate()
//重新连接一个未连接上的WIFI网络
reconnect()
//移除某一个网络
removeNetwork()
//保留一个配置信息
saveConfiguration()
//让一个连接有效
setWifiEnabled()
//开始扫描
startScan()
//更新一个网络连接
updateNetwork(WifiConfiguration config)3、获取WiFi状态
获取当前WiFi设备的状态,使用getWifiState()方法
int wifiStatus = mWifiManager.getWifiState();在WifiManager中定义了5中WiFi状态:
/**
* WIFI网卡正在关闭
*/
public static final int WIFI_STATE_DISABLING = 0;
/**
* WIFI网卡不可用
*/
public static final int WIFI_STATE_DISABLED = 1;
/**
* WIFI网卡正在打开
*/
public static final int WIFI_STATE_ENABLING = 2;
/**
* WIFI网卡可用
*/
public static final int WIFI_STATE_ENABLED = 3;
/**
* WIFI网卡状态不可知
*/
public static final int WIFI_STATE_UNKNOWN = 4;还可以调用isEnable()查看当前WiFi是否打开
boolean isEnable = mWifiManager.isWifiEnabled();4、扫描WiFi
扫描WiFi很简单,直接调用WifiManager的startScan()方法即可开启扫描,会有一个返回值,是否开启扫描。如果没有开启定位,startScan()总会返回false,无法扫描附近的WiFi。
boolean isStartScan = mWifiManager.startScan();该方法已经被废弃@deprecated,目前还能使用。官方文档说的是
Added in API level 1
Deprecated in API level 28
@deprecated The ability for apps to trigger scan requests will be removed in a future release.如果或得扫描结果呢?调用WifiManager的getScanResults(),返回扫描结果的集合。
List<ScanResult> mWifiScanResult = mWifiManager.getScanResults();ScanResult也定义在android.net.wifi包中,打印ScanResult对象可以看到扫描到的WiFi信息:
SSID: 106, //网络的名字,唯一区别WIFI网络的名字
BSSID: 8c:a6:df:2e:5a:74, //接入点的地址
capabilities: [WPA2-PSK-CCMP][WPA-PSK-CCMP][ESS], //网络接入的性能
level: -61, //所发现的WIFI网络信号强度
frequency: 2412, //当前WIFI设备附近热点的频率(MHz)
timestamp: 247485116699,
distance: ?(cm),
distanceSd: ?(cm),
passpoint: no,
ChannelBandwidth: 1,
centerFreq0: 2422,
centerFreq1: 0,
80211mcResponder: is not supported,
Carrier AP: no,
Carrier AP EAP Type: -1,
Carrier name: null,
Radio Chain Infos: [RadioChainInfo: id=0, level=-61]通过ScanResult对象也可以获取这些信息,ScanResult并没有提供这些字段的getter方法,因为这些字段在ScanResult类中的定义都是public的,可以直接访问。
不过不推荐startScan()之后,立马调用getScanResult()去获取结果。这样获取到的是上一次扫描的结果,而应该监听扫描结束发出的广播,扫描结束后再去获取结果。
首先定义一个广播接收器:
private BroadcastReceiver mWifiScanResultReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (null != action && action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
//接收到扫描结束发出的广播,获取WiFi扫描结果
mWifiScanResult = mWifiManager.getScanResults();
}
}
};onResume中注册广播接收器,接收WifiManager.SCAN_RESULTS_AVAILABLE_ACTION广播:
@Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
registerReceiver(mWifiScanResultReceiver, intentFilter);
}最后,别忘了在onPause()中反注册广播接收器:
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mWifiScanResultReceiver);
}Tips:事实证明开启热点后,可以扫描WiFi,但是始终是空[]。似乎不开启WiFi也能扫描附近的WiFi信息。
5、WifiInfo:连接信息
android.net.wifi包中定义了很多有用的类,其中WifiInfo类描述活动的或正在建立的任何Wifi连接的状态。
WifiInfo wifiInfo = mWifiManager.getConnectionInfo();调用getConnectionInfo()获取当前WiFi连接的信息。如果没有任何连接建立,也不会返回空的WiFiInfo而是返回一个默认的WiFiInfo对象。
//没有任何连接返回的WiFiInfo打印出来
SSID: <unknown ssid>,
BSSID: <none>,
MAC: 02:00:00:00:00:00,
Supplicant state: DISCONNECTED,
RSSI: -127,
Link speed: -1Mbps,
Frequency: -1MHz,
Net ID: -1,
Metered hint: false,
score: 0其中的字段可以通过WiFiInfo对象获取:
获取BSSID属性
getBSSID()
获取客户端的连通性
getDetailedStateOf()
获取SSID 是否被隐藏
getHiddenSSID()
获取IP 地址
getIpAddress()
获取连接的速度
getLinkSpeed()
获取Mac 地址
getMacAddress()
getRssi()
获取SSID
getSSID()
获取具体客户端状态的信息
getSupplicanState()6、WiFiConfiguration
WifiConfiguration用来配置网络,配置有密码的WiFiWifiConfiguration方法:
/**
* 连接有密码的wifi.
*
* @param SSID ssid
* @param Password Password
* @return apConfig
*/
private WifiConfiguration setWifiParamsPassword(String SSID, String Password) {
WifiConfiguration apConfig = new WifiConfiguration();
apConfig.SSID = "\"" + SSID + "\"";
apConfig.preSharedKey = "\"" + Password + "\"";
//不广播其SSID的网络
apConfig.hiddenSSID = true;
apConfig.status = WifiConfiguration.Status.ENABLED;
//公认的IEEE 802.11验证算法。
apConfig.allowedAuthAlgorithms.clear();
apConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
//公认的的公共组密码
apConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
apConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
//公认的密钥管理方案
apConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
//密码为WPA。
apConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
apConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
//公认的安全协议。
apConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
return apConfig;
}连接没有密码wifi
/**
* 连接没有密码wifi.
*
* @param ssid ssid
* @return configuration
*/
private WifiConfiguration setWifiParamsNoPassword(String ssid) {
WifiConfiguration configuration = new WifiConfiguration();
configuration.SSID = "\"" + ssid + "\"";
configuration.status = WifiConfiguration.Status.ENABLED;
configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
configuration.allowedPairwiseCiphers
.set(WifiConfiguration.PairwiseCipher.TKIP);
configuration.allowedPairwiseCiphers
.set(WifiConfiguration.PairwiseCipher.CCMP);
configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
return configuration;
}7、WiFi连接
首先addNetwork()添加一个网络,返回这个网络的id,连接网络的时候需要用到。
int id = mWifiManager.addNetwork(WifiConfiguration config)该方法也被标记为deprecate,在Android Q API 29之后只会返回-1。
Android Q or above, this API will always return -1.然后enableNetwork(id,boolean),尝试连接指定id的网络。
/**
*the ID of the network as returned by addNetwork or getConfiguredNetworks.
*/
boolean enableResult = mWifiManager.enableNetwork(id, true);8、扫描频率限制(耗电)
扫描WiFi是一个很耗电的操作,Android在官方文档中也明确给出了限制说明。
相关资料:
../packages/apps/Settings /src/com/android/settings/wifi
../frameworks/opt/net/wifi//service/jni/
../system/connectivity/wificond
../system/connectivity/wifilogd/
../hardware/libhardware_legacy/include/hardware_legacy/
《Wifi模块—源码分析Wifi热点扫描(Android P)》


评论 (1)
更多WiFi状态变化,可以通过广播接收获取。参考海晨忆博客的代码段:
/** * Author: 海晨忆. * Date: 2018/1/10 * Desc: 网络广播监听 */ public class NetworkReceiver extends BroadcastReceiver { public static final int STATE1 = 1;//密码错误 public static final int STATE2 = 2;//连接成功 public static final int STATE3 = 3;//连接失败 public static final int STATE4 = 4;//正在获取ip地址 public static final int STATE5 = 5;//正在连接 @Override public void onReceive(Context context, Intent intent) { if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(intent.getAction())) { //密码错误广播,是不是正在获得IP地址 int linkWifiResult = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1); if (linkWifiResult == WifiManager.ERROR_AUTHENTICATING) { //密码错误 ToastUtils.showTipMsg(R.string.password_wrong); sendNetworkStateChange(new NetWorkInfo().setState(STATE1)); } SupplicantState supplicantState = intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE); NetworkInfo.DetailedState state = WifiInfo.getDetailedStateOf(supplicantState); Log.v(Constants.HTTP_WZ, state.name()); if (state == NetworkInfo.DetailedState.CONNECTING) { //正在连接 ToastUtils.showTipMsg(R.string.linking); sendNetworkStateChange(new NetWorkInfo().setState(STATE5)); } else if (state == NetworkInfo.DetailedState.FAILED || state == NetworkInfo.DetailedState.DISCONNECTING) { //连接失败 sendNetworkStateChange(new NetWorkInfo().setState(STATE3)); ToastUtils.showTipMsg(R.string.linked_failed); } else if (state == NetworkInfo.DetailedState.CONNECTED) { //连接成功 ToastUtils.showTipMsg(R.string.linked_success); } else if (state == NetworkInfo.DetailedState.OBTAINING_IPADDR) { //正在获取ip地址 sendNetworkStateChange(new NetWorkInfo().setState(STATE4)); } else if (state == NetworkInfo.DetailedState.IDLE) { //闲置的 ConnectivityManager connectManager = (ConnectivityManager) BaseApplication.getInstance() .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = null; if (connectManager != null) { activeNetworkInfo = connectManager.getActiveNetworkInfo(); } if (activeNetworkInfo == null) { sendNetworkStateChange(new NetWorkInfo().setState(STATE3)); ToastUtils.showTipMsg(R.string.linked_failed); } } } else if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) { // 监听wifi的打开与关闭,与wifi的连接无关 int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0); Log.v(Constants.HTTP_WZ, "wifiState:" + wifiState); switch (wifiState) { case WifiManager.WIFI_STATE_DISABLING://正在停止0 ToastUtils.showTipMsg(R.string.close_wifi); break; case WifiManager.WIFI_STATE_DISABLED://已停止1 break; case WifiManager.WIFI_STATE_UNKNOWN://未知4 break; case WifiManager.WIFI_STATE_ENABLING://正在打开2 ToastUtils.showTipMsg(R.string.opening_wifi); break; case WifiManager.WIFI_STATE_ENABLED://已开启3 break; default: break; } } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) { // 监听wifi的连接状态即是否连上了一个有效无线路由 Parcelable parcelableExtra = intent .getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); if (null != parcelableExtra) { // 获取联网状态的NetWorkInfo对象 NetworkInfo networkInfo = (NetworkInfo) parcelableExtra; //获取的State对象则代表着连接成功与否等状态 NetworkInfo.State state = networkInfo.getState(); //判断网络是否已经连接 boolean isConnected = state == NetworkInfo.State.CONNECTED; Log.v(Constants.HTTP_WZ, "isConnected:" + isConnected); if (isConnected) { ToastUtils.showTipMsg(R.string.linked_success); sendNetworkStateChange(new NetWorkInfo().setState(STATE2)); } } } } /** * 发送网络状态eventBus. * * @param info info */ private void sendNetworkStateChange(NetWorkInfo info) { EventBus.getDefault().post(info); } @SuppressWarnings("unused") private String getConnectionType(int type) { String connType = ""; if (type == ConnectivityManager.TYPE_MOBILE) { connType = "移动网络"; } else if (type == ConnectivityManager.TYPE_WIFI) { connType = "WIFI网络"; } return connType; } public static class NetWorkInfo { public int state;//1密码错误,2:连接成功,3:连接失败 private NetWorkInfo setState(int state) { this.state = state; return this; } } }