三月份新入职的公司,公司的主要业务是只能电视应用,因为公司战略须要,准备作一个tvos,里面包含有设置的系统应用,本人分到的任务是wifi这一块开发。因为以前接手的都是移动端的开发,对于wifi这一块几乎没有接触过,因此分享出来,但愿我踩过的坑,你们能避免。android
wifi扫描:bash
首先,要对设备的网络状况就行修改,就要得到相应的权限:网络
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /><!-- 容许程序改变网络连接状态 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><!--容许程序访问访问WIFI网络状态信息 -->复制代码
而后,接收网络变化的广播:ide
public class WifiBroadcastReceiver extends BroadcastReceiver {
private IntentFilter filter;
private Context context;
private WifiStateChangeListener wifiStateChangeListener;
public WifiBroadcastReceiver(Context context) {
this.context = context;
filter = new IntentFilter();
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);//wifi开关变化广播
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);//热点扫描结果通知广播
filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);//—热点链接结果通知广播
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); //—网络状态变化广播(与上一广播协同完成链接过程通知)
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(this, filter);
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (null != wifiStateChangeListener) {
wifiStateChangeListener.onWifiChange(intent);
}
}
public void unRegister() {
context.unregisterReceiver(this);
}
public void setWifiStateChangeListener(WifiStateChangeListener wifiStateChangeListener) {
this.wifiStateChangeListener = wifiStateChangeListener;
}
public interface WifiStateChangeListener {
/**
*/
void onWifiChange(Intent action);
}
}复制代码
经过不一样广播的action,进行相应的处理:测试
@Override
public void onWifiChange(Intent action) {
switch (action.getAction()) {
case WifiManager.WIFI_STATE_CHANGED_ACTION:
checkWfiState(action);
break;
case WifiManager.SCAN_RESULTS_AVAILABLE_ACTION:
getScanResult();
break;
case WifiManager.SUPPLICANT_STATE_CHANGED_ACTION:
int error = action.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, 0);
if (WifiManager.ERROR_AUTHENTICATING == error) {
toastFail();
} else {
checkWifiConnectState(action);
}
break;
default:
//网线
if (NetInfoUtil.isEthernet()) {
finish();
}
break;
}
}
复制代码
接下来打开wifi,ui
//开启或者关闭WIFI
public void openWifi(boolean isEnable) {
if (!isEnable) {
removeConnecting();
}
wifiManager.setWifiEnabled(isEnable);
}复制代码
若是wifi以及打开就开始扫描wifi信号:this
private void checkWfiState(Intent intent) {
//获取当前wifi的状态
int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_DISABLED);
switch (wifiState) {
case WifiManager.WIFI_STATE_DISABLED:
//wifi已关闭
connectState = WifiConnectState.NONE;
wifiSwitchView.setEnabled(true);
wifiSwitchView.setWifiOpened(false);
stopScanWifi();
break;
case WifiManager.WIFI_STATE_ENABLED:
//wifi已打开
startScanWifi(true);
wifiSwitchView.setEnabled(true);
wifiSwitchView.setWifiOpened(true);
break;
case WifiManager.WIFI_STATE_ENABLING:
wifiSwitchView.setEnabled(false);
//wifi正在打开
break;
case WifiManager.WIFI_STATE_DISABLING:
wifiSwitchView.setEnabled(false);
//wifi正在关闭
break;
default:
break;
}
}复制代码
开始扫描wifi:加密
/**
* 开始扫描Wifi
*/
private void startScanWifi(boolean isLoading) {
if (isLoading) {
showLoadingDialog("");
}
wifiManager.startScan();
}复制代码
Wifi扫描成功以后,会发出一个WifiManager.SCAN_RESULTS_AVAILABLE_ACTIONspa
的广播。经过List<ScanResult> scanResults = wifiManager.getScanResults();code
获取扫描结果,接下来看下ScanResult这个类,其中SSID,表示是wifi的名称。capabilities描述了wifi的加密方式等信息,能够经过以下方式判断wifi的加密方式:
if (model.capabilities.contains("WPA") || model.capabilities.contains("wpa")) {
encryptType = TYPE_ENCRYPT_WPA;
} else if (model.capabilities.contains("WEP") || model.capabilities.contains("wep")) {
encryptType = TYPE_ENCRYPT_WEP;
} else {
encryptType = "";
}复制代码
不一样的加密方式,链接的处理方式第不同的。level是表示wifi的信号强度,通常是个负数,越大表示信号越强。
wifi链接:
首先判读wifi是否已保存,若是以及保存,那么不须要输入密码直接链接,经过以下方法判读wifi是否保存:
private WifiConfiguration getExistConfig(String ssid) {
List<WifiConfiguration> existingConfigs = wifiManager.getConfiguredNetworks();
if (!CollectionUtil.isEmpty(existingConfigs)) {
for (WifiConfiguration existingConfig : existingConfigs) {
if (TextUtil.isEquals(existingConfig.SSID, ssid))
return existingConfig;
}
}
return null;
}复制代码
wifiManager.getConfiguredNetworks()会把全部已经保存的wifi,返回,若是扫描出的wifi吗名称,可以在.getConfiguredNetworks()找到证实是已保存的。经过以下方法链接wifi:
int netId = wifiManager.addNetwork(config);
//WifiManager的enableNetwork接口,就能够链接到netId对应的wifi了
//其中boolean参数,主要用于指定是否须要断开其它Wifi网络
wifiManager.enableNetwork(netId, true);复制代码
若是wifi没有保存,那么有本身构造WifiConfiguration,以下:
public WifiConfiguration createWifiConfig(WifiScanResultVM wifiScanResultVM) {
//初始化WifiConfiguration
WifiConfiguration config = new WifiConfiguration();
config.SSID = wifiScanResultVM.getSsid();
//不须要密码的场景
String password = wifiScanResultVM.getPassWord();
if (!wifiScanResultVM.isEncrypt()) {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
//以WEP加密的场景
} else if (TextUtil.isEquals(wifiScanResultVM.getEncryptType(), WifiScanResultVM.TYPE_ENCRYPT_WEP)) {
int i = password.length();
if (((i == 10 || (i == 26) || (i == 58))) && (password.matches("[0-9A-Fa-f]*"))) {
config.wepKeys[0] = password;
} else {
config.wepKeys[0] = "\"" + password + "\"";
}
//以WPA加密的场景,本身测试时,发现热点以WPA2创建时,一样能够用这种配置链接
} else if (TextUtil.isEquals(wifiScanResultVM.getEncryptType(), WifiScanResultVM.TYPE_ENCRYPT_WPA)) {
config.preSharedKey = "\"" + password + "\"";
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
}
return config;
}复制代码
须要区分不一样的加密方式,链接过程当中会屡次发出,WifiManager.SUPPLICANT_STATE_CHANGED_ACTION
这个广播,处理以下:
int error = action.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, 0);
if (WifiManager.ERROR_AUTHENTICATING == error) {
toastFail();
} else {
checkWifiConnectState(action);
}复制代码
/**
* wifi切换链接
*
* @param intent
*/
public void checkWifiConnectState(Intent intent) {
SupplicantState supplicantState = intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
if (null != supplicantState) {
switch (supplicantState) {
case DISCONNECTED:
connectState = WifiConnectState.NONE;
getScanResult();
break;
case COMPLETED:
// if (currentData != null) {
// XLog.e("SSID", "checkWifiConnectState" + currentData.getSsid());
// }
getScanResult();
connectState = WifiConnectState.CONNECTED;
break;
}
return;
}
}复制代码
还有一个问题,如何判断当前正在链接的是哪一个wifi,以下:
private String getCurrentSsid() {
// XLog.e("SSID", "getCurrentSsid: ");
if (connectState != WifiConnectState.CONNECTED) {
// XLog.e("SSID", " WifiConnectState.CONNECTED: " + connectState.name());
return "";
}
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
if (wifiInfo != null) {
// XLog.e("SSID", "wifiInfo" + wifiInfo.getSSID());
return wifiInfo.getSSID();
}
// XLog.e("SSID", "null null null ");
return "";
}复制代码
经过拿到当前wifi的链接信息进行比对,有个问题值得注意,当前wifi正在链接时,
WifiInfo wifiInfo = wifiManager.getConnectionInfo();复制代码
返回的也是有值得,须要注意下。
未填的坑:
当有两个wifi名称同样的时候,就很差处理,这边的处理是直接忽略一个,若是有大神有好的方案,欢迎留言,你们一块儿讨论下