Android 扫描周边wifi并连接到指定wifi
本篇文章主要记录一下Android8.0上开启wifi并扫描周边wifi,获取周边wifi列表,再通过RecyclerView进行显示,通过RecyclerView item的点击事件进行wifi连接,并完成一系列的连接过程。
添加权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
注册广播接收wifi扫描结果
//注册WiFi扫描结果、WiFi状态变化的广播接收
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); //监听wifi扫描结果
intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); //监听wifi是开关变化的状态
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); // 监听wifi是否连接成功的广播
registerReceiver(wifiReceiver, intentFilter);
申请权限
不要问我为啥要申请权限,干就完了,权限申请,这个我就略过了。。。
自行处理权限被拒绝的情况
得到用户授权后,初始化WifiManager
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
开启wifi
private void enableWifi() {
if (wifiManager != null && !wifiManager.isWifiEnabled()) {
//WiFi未打开,开启wifi
wifiManager.setWifiEnabled(true);
}
}
完成以上步骤就可以在广播接收器里面收到wifi开启的广播,代码如下:
//WiFi状态变化广播:如果WiFi已经完成开启,即可进行WiFi扫描
int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0);
switch (wifiState) {
case WifiManager.WIFI_STATE_ENABLED:
MyLog.log(TAG, "**********wifi开启成功,开始扫描周边网络***********");
// 这里可以自定义一个扫描等待对话框,待完成扫描结果后关闭
Objects.requireNonNull(wifiManager).startScan();
break;
case WifiManager.WIFI_STATE_DISABLED:
ToastUtil.showToast(context, "wifi开关未打开");
break;
}
待扫描完成后就可以接受到扫描结果了
//扫描结果广播,查找扫描列表是否存在指定SSID的WiFi,如果存在则进行连接
List<ScanResult> scanResultList = Objects.requireNonNull(wifiManager).getScanResults();
点击item项执行连接
public WifiConfiguration createWifiConfig(String ssid, String password, WifiCipherType type) {
WifiConfiguration config = new WifiConfiguration();
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
config.SSID = "\"" + ssid + "\"";
if (type == WifiCipherType.WIFICIPHER_NOPASS) {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
}
if (type == WifiCipherType.WIFICIPHER_WEP) {
config.preSharedKey = "\"" + password + "\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
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;
}
if (type == WifiCipherType.WIFICIPHER_WPA) {
config.preSharedKey = "\"" + password + "\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
config.status = WifiConfiguration.Status.ENABLED;
}
return config;
}
判断该wifi热点的加密类型
/**
* 判断wifi热点支持的加密方式
*/
public WifiCipherType getWifiCipher(String capabilities) {
if (capabilities.isEmpty()) {
return WifiCipherType.WIFICIPHER_INVALID;
} else if (capabilities.contains("WEP")) {
return WifiCipherType.WIFICIPHER_WEP;
} else if (capabilities.contains("WPA") || capabilities.contains("WPA2") || capabilities.contains("WPS")) {
return WifiCipherType.WIFICIPHER_WPA;
} else {
return WifiCipherType.WIFICIPHER_NOPASS;
}
}
下面给出枚举类
public enum WifiCipherType {
WIFICIPHER_WEP, WIFICIPHER_WPA, WIFICIPHER_NOPASS, WIFICIPHER_INVALID
}
如果不出意外不负众望密码也没错的话你就可以连接成功了。。。是不是很开心啊!然而这一切才刚刚开始,哈哈哈…
试想 一下:当用户不小心手抖了一下输错密码了,又或者密码输入成功了,又连接上其他热点了,再切回来该这么办?
先说一下wifi连接的套路吧!当用户连接一个wifi热点时,不管输入的密码是否正确那么系统默认会记住该密码,也就是说默认情况下当用户连接一个指定的wifi热点时,系统会默认记住这个密码并记录到系统里面去(当然如果你愿意每次连接都输入密码也无可厚非)下次再切换到这个热点上就可以重用这个配置进行重新连接而省去输入密码的麻烦。
首先说一下我的思路:我觉得如果用户输入了错误的密码,那么我就从网络配置列表中查询出该配置信息并移除,并提示用户连接失败!那么当用户再次点击该热点时再弹出密码输入框,或者直接弹出密码输入框让用户重新输入密码,具体业务逻辑开发者自行实现,那么我们看看该如何获取网络配置列表呢?非常简单,先看看系统源码,如下:
/**
* Return a list of all the networks configured for the current foreground
* user.
* Not all fields of WifiConfiguration are returned. Only the following
* fields are filled in:
* <ul>
* <li>networkId</li>
* <li>SSID</li>
* <li>BSSID</li>
* <li>priority</li>
* <li>allowedProtocols</li>
* <li>allowedKeyManagement</li>
* <li>allowedAuthAlgorithms</li>
* <li>allowedPairwiseCiphers</li>
* <li>allowedGroupCiphers</li>
* </ul>
* @return a list of network configurations in the form of a list
* of {@link WifiConfiguration} objects.
*/
public List<WifiConfiguration> getConfiguredNetworks() {
try {
ParceledListSlice<WifiConfiguration> parceledList =
mService.getConfiguredNetworks();
if (parceledList == null) {
return Collections.emptyList();
}
return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
这是WifiManager的函数直接调用就可以了,返回的正是我们需要的wifi网络配置列表,过滤我们需要移除的ssid就搞定了,先看一下系统是如何忘记一个wifi热点的
/**
* Remove the specified network from the list of configured networks.
* This may result in the asynchronous delivery of state change
* events.
*
* Applications are not allowed to remove networks created by other
* applications.
*
* @param netId the ID of the network as returned by {@link #addNetwork} or {@link
* #getConfiguredNetworks}.
* @return {@code true} if the operation succeeded
*/
public boolean removeNetwork(int netId) {
try {
return mService.removeNetwork(netId, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
没错正是通过networkId,那么这个networkId是什么,正是WifiConfiguration里面的一个成员变量,既然我们拿到了网络配置列表,那么想要拿到这个networkId那就轻而易举了,同样是通过WifiManager的removeNetWork函数移除已经配置过的连接信息,下面给出移除wifi热点的配置信息,代码如下:
wifimanager.removeNetwork(networkId);
清除已连接的信息我们搞定了,还剩下一个使用已经配置过的信息进行重新连接,我们一起来实现吧!刚才说到,既然我们已经可以查询到该wifi热点是否已经配置过了,那么如果已经配置过了我们就获取配置信息进行重连,具体实现如下:
/**
* 根据已有配置信息接入某个wifi热点
*/
public boolean addNetWork(WifiConfiguration config) {
WifiInfo wifiinfo = manager.getConnectionInfo();
if (null != wifiinfo) {
manager.disableNetwork(wifiinfo.getNetworkId());
}
boolean result;
if (config.networkId > 0) {
result = manager.enableNetwork(config.networkId, true);
manager.updateNetwork(config);
} else {
int i = manager.addNetwork(config);
result = false;
if (i > 0) {
manager.saveConfiguration();
return manager.enableNetwork(i, true);
}
}
MyLog.log(TAG, " result:" + result);
return result;
}
以上就完成了wifi热点的切换,那么还有最后一种情况是没有密码的wifi热点该怎么连接呢?代码如下:
createWifiConfig(ssid, null, WifiSupport.WifiCipherType.WIFICIPHER_NOPASS);
调用上面第七段代码,密码直接传null就可以了,是不是很简单呢?
其实这过程真的没有想象的那么简单,因为我是一步一步踏坑过来的,因为直接调用系统wifi设置界面来完成以上功能真的炒鸡简单,而且也不用考虑多种场景,因为所有的应用场景google那帮老头都为我们考虑全了,过程虽是痛苦的但结果还是值得深思的,哈哈哈哈啊哈哈…
编后记
本篇博文是本人的开篇作,有遗漏和错误还望Android界的大佬以及前辈不吝指教,同时本篇博文也只是告诉大家一种实现思路,具体业务逻辑还得自行参照实践!感谢大家的阅读,你的关注和转发是我无限的动力…