需求:本人搞物联网,wifi需要上网,前提需要先让wifi模组连接上路由器,存在wifi模组配网的过程。
有两种配网模式,
SmartLink,一种使用UDP广播技术实现的一种快速配网的技术,改技术有一个致命的缺陷,当路由不支持UPD广播,或者被封禁的时候,改技术无法使用了。
SoftAP,这种比较传统的方式,先让手机与wifi模组建立的热点进行连接,将需要连接的wifi路由 ssid与password告知wifi模组后,让wifi模组去连接。兼容性很好,贵在比较复杂,操作步骤多。
对于Android系统来说,因为可以对wifi进行操作,可以做到与SmartLink一样的快捷,更多的交给后台处理完成配网;将使用到WifiManager这个系统类;
坑一:WifiManager获取时产生内存泄露
引用前辈的帖子, //www.greatytc.com/p/5d96983fc6db
坑二:addNetwork返回-1
相信很多人在这块开发的时候都不会自己一个一个字母的敲出来,网上copy一下完成了。
基本上网上的代码就这么一写,注意红框中的两个方法;
你会发现在很多请情况下addNetword都是返回-1;这将会使enableNetword 无法连接到指定的wifi中;
官方APP描述,新网络描述添加到已配置网络集。也就是说这个方法用于新的,未连接过的wifi;适当换成
//判断wifi曾经是不是连接过
WifiConfiguration tempConfig = isExsits(ssid);
if (tempConfig != null) {
boolean enabled = wifiManager.enableNetwork(tempConfig.networkId, true);
Log.d(TAG, "enableNetwork status enable=" + enabled);
} else {
int netID = wifiManager.addNetwork(wifiConfig);
boolean enabled = wifiManager.enableNetwork(netID, true);
Log.d(TAG, "enableNetwork status enable=" + enabled);
}
private WifiConfiguration isExsits(String SSID) {
if (wifiManager != null) {
List<WifiConfiguration> existingConfigs = wifiManager
.getConfiguredNetworks();
for (WifiConfiguration existingConfig : existingConfigs) {
if (existingConfig.SSID.equals("\"" + SSID + "\"")) {
return existingConfig;
}
}
}
return null;
}
坑三:扫描获取手机附近wifi列表返回 size=0
/**
* 获取wifi列表
*
* @return
*/
private List<ScanResult> getWifiList() {
// 开始扫描
wifiManager.startScan();
// 得到扫描结果
return wifiManager.getScanResults();
}
坑四:连接没有密码的AP热点
把taget版本支持到28之后,发现连接AP热点怎么也连不上了,经过一段时间的测试,发现AP热点没有密码的时候, addnetword方法必然返回-1. 日了啊
搞了蛮久,WifiConfiguration的配置得增加上 “无密码” “WPA” “WEP”加密方式的配置,顺利搞定;
if (/*Open network*/) {
// No security
wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
wifiConfig.allowedAuthAlgorithms.clear();
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
} else if (/*WPA*/ || /*WPA2*/) {
//WPA/WPA2 Security
wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
wifiConfig.preSharedKey = "\"".concat(password).concat("\"");
} else if (/*WEP*/) {
// WEP Security
wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
if (getHexKey(password)) wifiConfig.wepKeys[0] = password;
else wifiConfig.wepKeys[0] = "\"".concat(password).concat("\"");
wifiConfig.wepTxKeyIndex = 0;
}
在Android 6.0之后,必须打开GPS才可以获取到wifi列表,想办法让客户打开GPS开关把,完美解决;
(目前miui 系统,还是无法正常连接到指定的wifi,打印log显示已经连接了,实际手机没有切换过去,不知道什么问题,有遇到这样问题的,希望可以指导下我;)引用评论3楼的方法,在连接之前先disconnect,再连接,解决;感谢!!!
2019-11-04 继续补充
坑五 关于 MIUI、华为 disconnect之后 积极连接其他wifi问题的处理
小米与华为手机断开连接后,会非常快速的连接曾经连接过的WIFI,这时 enableNetwork 方法压根抢不过系统,导致连接AP 热点失败。
引用前辈一篇文章,代码写得比较全了,里面注释也提到了这个问题 。
https://juejin.im/post/5b18c3d1f265da6e3c6b93da
说下我解决这个问题的过程,网上其实有一些极端的方法可以解决这个问题。
1、WiFiManager.disableNetwork()
使用 disableNetwork 方法,该方法是讲已经保存的wifi进行禁用,禁用之后,系统就不会自动的去连接这些连接过的WIFI,解决这个问题(华为测试过之后);
(小米去测试的时候就恶心了,MIUI系统每次操作WIFI都会提示用户,是否允许操作wifi,也就是说你保存了100个wifi的配置你得点100次允许,了勒个去!!!)
2、WifiManager.WifiLock
没法子,又得去爬谷歌的API文档了,英语不好,折磨。。。
主要使用以上三个方法,给WIFI加锁。
public class WifiLocKManager {
// 定义WifiManager对象
private WifiManager mWifiManager;
// 定义一个WifiLock
private WifiManager.WifiLock mWifiLock;
private Context context;
public WifiLocKManager(Context context) {
// 取得WifiManager对象
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
//第一种方式
creatWifiLock("WifiLocKManager");
//第二种方式
//解析一下三个常量
//WIFI_MODE_SCAN_ONLY 在此Wi-Fi锁定模式下,Wi-Fi将保持活动状态,但是唯一受支持的操作是启动扫描以及随后报告扫描结果。
// 不会尝试自动连接到记住的访问点,也不会自动执行定期扫描以查找记住的访问点。在这种模式下,应用程序必须明确请求扫描。
//····································································
//WIFI_MODE_FULL_LOW_LATENCY
//在此Wi-Fi锁定模式下,Wi-Fi将优先运行以实现低延迟
//低延迟模式进行了优化,以减少数据包延迟,因此,在进行权衡时,其他性能指标可能会受到影响
//
//WIFI_MODE_FULL_HIGH_PERF
//在此Wi-Fi锁定模式下,Wi-Fi不会节电。这样可以降低数据包延迟。仅当设备连接到接入点时,锁才处于活动状态。
//即使设备屏幕关闭或获取应用程序在后台运行,该锁定也处于活动状态。此模式将消耗更多功率,因此仅在需要此折衷时才应使用。
//
creatWifiLock("WifiLocKManager", WifiManager.WIFI_MODE_SCAN_ONLY);
}
public void creatWifiLock(String locakName, int lockType) {
mWifiLock = mWifiManager.createWifiLock(lockType, locakName);
}
/**
* * 创建一个WifiLock
* *
* * @param locakName 名称
*
*/
public void creatWifiLock(String locakName) {
mWifiLock = mWifiManager.createWifiLock(locakName);
}
/**
* * 锁定WifiLock
*
*/
public void acquireWifiLock() {
mWifiLock.acquire();
}
/**
* * 解锁WifiLock
*
*/
public void releaseWifiLock() {
// 判断时候锁定
if (mWifiLock.isHeld()) {
mWifiLock.release();
}
}
}