摘要:最近遇到需要自己把WiFi的界面重新定义一下。并进行可以连接。在这里进行记录一下,方便后面的查找
对应WiFi的使用的想到的肯定是WifiManager,因为各种各样的 Manager提供简单方便的使用,在看见WifiManager的看是就基本上说明由那些作用。
It deals with several categories of items:
`it should only be obtained from an application context, and not from any other derived context to avoid memory leaks within the calling process.
hah,我也是通过必应翻译的,意思是我们要用 context.getApplicationContext(),而不是其子类,因为这样可能造成内存溢出。在后面的版本(N)中,这个问题系统自身就解决了。
在实例化好WifiManager对象后,调用startScan( )方法就开始进行扫描,再调用getScanResults( )。就会返回搜索到的WiFi信息,返回一个List 。ScanResult类中,我用到的只有 SSID(热点的名称),capabilities(包含了密码为何种的加密方式),level(信号的强度)。
这样就好了。答案却不是。在通过 WifiManager.isWifiEnabled()的方法判断wifi是否打开之后,用WifiManager.setWifiEnabled(true)或false,去开关wifi。关闭wifi重新打开之后返回的扫描结果长度为 0。为此不得不提到 WifiManager中的几个广播了。在这些广播中可以返回各种数据,有的广播可以返回几个数据,通过不key去获取。
WifiManager中几个用到的广播广播(action) | 作用 | 获取返回值的key | 返回值 |
WIFI_STATE_CHANGED_ACTION = “android.net.wifi.WIFI_STATE_CHANGED” | 指示已启用 wi-fi、禁用、启用、禁用或未知。 |
|
|
SUPPLICANT_CONNECTION_CHANGE_ACTION = “android.net.wifi.supplicant.CONNECTION_CHANGE” | 已经完全连接或者已经完全失去连接所触发的广播。因为我在打开WiFi,他自动连接的时候就调用了 | EXTRA_SUPPLICANT_CONNECTED | getBooleanExtra(String,boolean)。 为true, 表示当前已成功连接, 反之, 则是断开了连接。 |
NETWORK_STATE_CHANGED_ACTION | wi-fi连接的状态已更改,以 NetworkInfo对象的形式返回,如果新的状态已连接,则额外的提供接入点BSSID和WifiInfo |
|
|
SUPPLICANT_STATE_CHANGED_ACTION = “android.net.wifi.supplicant.STATE_CHANGE” | 明建立连接的状态访问点已更改,返回一个SupplicantState。请求状态是 wi-fi 特定的, 如果只是对连接的总体状态感兴趣, 则通常不是最有用的东西。在测试时候在SUPPLICANT_CONNECTION_CHANGE_ACTION之前调用。 |
|
|
SCAN_RESULTS_AVAILABLE_ACTION = “android.net.wifi.SCAN_RESULTS” | 访问点扫描已完成, 并可从请求者那里获得结果。调用getScanResults ()以获取结果 EXTRA_RESULTS_UPDATED 指示扫描是否成功完成。 | getScanResults() | |
RSSI_CHANGED_ACTION = “android.net.wifi.RSSI_CHANGED” | 信号强度改变 | getIntExtra(WifiManager.EXTRA_NEW_RSSI, 0) | |
NETWORK_IDS_CHANGED_ACTION = “android.net.wifi.NETWORK_IDS_CHANGED” | 已配置网络的网络 id 可能已更改。 |
可以通过WIFI_STATE_CHANGED_ACTION广播去获取当前wifi的状态,在状态为 WIFI_STATE_ENABLE情况下使用startScan( )方式,开启搜索。在SCAN_RESULTS_AVAILABLE_ACTION广播中去获取这个搜索的结果。使用NETWORK_STATE_CHANGED_ACTION去获取到这个wifi连接的一状态。其中wifi状态由五种分别是:WIFI_STATE_DISABLED ,WIFI_STATE_DISABLING ,WIFI_STATE_ENABLED ,WIFI_STATE_ENABLING ,WIFI_STATE_UNKNOWN。而NetworkInfo的状态通过getState()去得到NetworkInfo.State 。NetworkInfo.State.DISCONNECTED表示连接已断开。NetworkInfo.State.CONNECTED表示连接完成。通过wifiManager.getConnectionInfo()获得WifiInfo,再通过getSSID( )获取到对应的热点名称。其中,NetworkInfo.State还有几种其它状态。还可有更加具体状态信息,通过 NetworkInfo.DetailedState stateD = info.getDetailedState(),去获取,其中有很多状态,如:IDLE(空闲),SCANNING(正在扫描),CONNECTING(连接中),AUTHENTICATING(正在验证身份信息),OBTAINING_IPADDR(正在获取IP地址),CONNECTING(连接中),CONNECTED(已连接),等等,等等。一系列。而且后面的还在增加。如:VERIFYING_POOR_LINK(暂时关闭(网络状况不佳))需要API16,CAPTIVE_PORTAL_CHECK(判断是否需要浏览器二次登录)API17。
以下是我关闭wifi和打开wifi,以上几个广播打印的 Log
//关闭wifi
I/WifiAdminUtil ->: BroadCastReceiver NETWORK_STATE_CHANGED_ACTION
I/WifiAdminUtil ->: BroadCastReceiver NETWORK_STATE_CHANGED_ACTION
I/WifiAdminUtil ->: BroadCastReceiver WIFI_STATE_CHANGED_ACTION
extra = 3
I/WifiAdminUtil ->: BroadCastReceiver SUPPLICANT_STATE_CHANGED_ACTION
err = 0
I/WifiAdminUtil ->: BroadCastReceiver SUPPLICANT_CONNECTION_CHANGE_ACTION
extra = false
I/WifiAdminUtil ->: BroadCastReceiver WIFI_STATE_CHANGED_ACTION
extra = 0
//开启wifi
I/WifiAdminUtil ->: start scan millis1503825827873
I/WifiAdminUtil ->: BroadCastReceiver WIFI_STATE_CHANGED_ACTION
extra = 1
I/WifiAdminUtil ->: BroadCastReceiver WIFI_STATE_CHANGED_ACTION
extra = 2
I/WifiAdminUtil ->: BroadCastReceiver SUPPLICANT_CONNECTION_CHANGE_ACTION
extra = true
I/WifiAdminUtil ->: BroadCastReceiver SUPPLICANT_STATE_CHANGED_ACTION
err = 0
I/WifiAdminUtil ->: BroadCastReceiver SCAN_RESULTS_AVAILABLE_ACTION
I/WifiAdminUtil ->: BroadCastReceiver SUPPLICANT_STATE_CHANGED_ACTION
err = 0
I/WifiAdminUtil ->: BroadCastReceiver SUPPLICANT_STATE_CHANGED_ACTION
err = 0
I/WifiAdminUtil ->: BroadCastReceiver SUPPLICANT_STATE_CHANGED_ACTION
err = 0
I/WifiAdminUtil ->: BroadCastReceiver NETWORK_STATE_CHANGED_ACTION
I/WifiAdminUtil ->: BroadCastReceiver SUPPLICANT_STATE_CHANGED_ACTION
err = 0
I/WifiAdminUtil ->: BroadCastReceiver RSSI_CHANGED_ACTION
extra = -38
I/WifiAdminUtil ->: BroadCastReceiver SUPPLICANT_STATE_CHANGED_ACTION
I/WifiAdminUtil ->: BroadCastReceiver RSSI_CHANGED_ACTION
extra = -38
I/WifiAdminUtil ->: BroadCastReceiver NETWORK_STATE_CHANGED_ACTION
I/WifiAdminUtil ->: BroadCastReceiver NETWORK_STATE_CHANGED_ACTION
I/WifiAdminUtil ->: BroadCastReceiver NETWORK_STATE_CHANGED_ACTION
从以上获取搜索的结果后,我们就可以进行连接。在连接之前先查看一下断开的连接的操作。不破不立吗,没有断开之前的连接。也就不能有新的连接了。断开通过mWifiManager.disconnect()
就可以断开了。在手机还有一个忘记密码的操作,其忘记连接密码可以通过如下就实现:
/**
* 判断扫描的的热点的加密方式,通过 capabilities字段
*
* @param scanResult
* @return
*/
public String encryptionMetho(ScanResult scanResult) {
//在demo和我自己在WifiConfiguration KeyMgmt 中看见的不一样
/**
* 在自己的测试中,如果没有密码的公共热点。capabilities = [ESS],其余都有各种加密方式在前面
*
* 其中源于百度的引用 https://zhidao.baidu.com/question/197383906.html
* 密码的方式共有 3种,WEP , WPA , WPA2
* WEP 的密码架构有缺陷,很容易让无线黑客攻击,不建议使用
* WPA , WAP2 都是很强壮的密码,目前的破解难度很大(当然,原密码不能过于的简单)
* 至于 AES , TKIP , TKIP&AES 这些都是 wap的加密算法
*/
String desc = "";
String descOri = scanResult.capabilities;
if (!descOri.equals("")|| !TextUtils.isEmpty(descOri)|| descOri != null) {
if (descOri.toUpperCase().contains("WPA-PSK")) {
desc = "WPA";
} else if (descOri.toUpperCase().contains("WPA2-PSK")) {
desc = "WPA2";
} else if (descOri.toUpperCase().contains("WPA-PSK") && descOri.toUpperCase().contains("WPA2-PSK")) {
desc = "WPA/WPA2";
} else if (descOri.toUpperCase().contains("WEP")) { //很少使用
desc = "WEP";
} else {
desc = "";
}
}
return desc ;
}
变量名 | 含义 |
SSID | 热点的名称 |
preSharedKey | 热点的密码 |
hiddenSSID | 是否隐藏SSID |
status | 是否启用这用这个热点配置 |
allowedAuthAlgorithms | IEEE 802.11认证算法 OPEN |
allowedGroupCiphers | 组秘钥TKIP+CCMP |
allowedPairwiseCiphers | 对称秘钥TKIP+CCMP |
allowedKeyManagement | 秘钥管理WPA_PSK |
allowedProtocols | 加密协议WPA+RSN |
/**
* 创建一个 WifiConfiguration,其参数配置看 connect里面的注释
*
* @return
*/
private WifiConfiguration createWifiInfo(String ssid, String psk, int type) {
WifiConfiguration config = new WifiConfiguration();
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
config.SSID = "\"" + ssid + "\"";
//查看以前是否也配置过这个网络
WifiConfiguration tempConfig = isExsits(ssid);
if (tempConfig != null) { //以前配置过,就移除以前的
mWifiManager.removeNetwork(tempConfig.networkId);
}
//三种加密方式,没有密码,wpa,wep
if (type == TYPE_NO_PASSWD) {
// config.wepKeys[0] = "";
// config.wepTxKeyIndex = 0;
//有的博客为以上两句编写的话,不可以连接上没有密码的热点(在模拟器测试是这样的,真机未测)
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
} else if (type == TYPE_WEP) {
config.preSharedKey = "\"" + psk + "\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.wepTxKeyIndex = 0;
} else if (type == TYPE_WPA) {
//修改之后的配置
config.preSharedKey = "\"" + psk + "\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
config.status = WifiConfiguration.Status.ENABLED;
} else {
return null;
}
Log.i(TAG,"createWifiInfo "+"ssid = "+ssid+" , psk = "+psk+" ,type = "+type) ;
return config;
}
其中注意在没有密码的创建要把注释的两局注释掉,不然真的不能成功。上面里面还有两个方法用于判断之前是否配置过,和删除配置(系统方法)。其代码如下:
/**
* 循环遍历查出以前是否调加过该网络
*
* @param ssid 热点名称
* @return 如果添加过,则返回以前的配置
*/
private WifiConfiguration isExsits(String ssid) {
List networks = mWifiManager.getConfiguredNetworks();
for (WifiConfiguration network : networks) {
if (network.SSID.equals("\"" + ssid + "\"")) {
return network;
}
}
return null;
}
上面就准备好了一切,就可以开始连接了。步骤如下: