前言
三月份新入职的公司,公司的主要业务是只能电视应用,由于公司战略需要,准备做一个tvos,里面包含有设置的系统应用,本人分到的任务是wifi这一块开发。由于之前接手的都是移动端的开发,对于wifi这一块几乎没有接触过,所以分享出来,希望我踩过的坑,大家能避免。
正文
wifi扫描:
首先,要对设备的网络情况就行修改,就要获得相应的权限:
<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网络状态信息 -->复制代码
然后,接收网络变化的广播:
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,
//开启或者关闭WIFI
public void openWifi(boolean isEnable) {
if (!isEnable) {
removeConnecting();
}
wifiManager.setWifiEnabled(isEnable);
}复制代码
如果wifi以及打开就开始扫描wifi信号:
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_ACTION
的广播。通过List<ScanResult> scanResults = wifiManager.getScanResults();
获取扫描结果,接下来看下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名称一样的时候,就不好处理,这边的处理是直接忽略一个,如果有大神有好的方案,欢迎留言,大家一起讨论下