三月份新入職的公司,公司的主要業務是隻能電視應用,因爲公司戰略須要,準備作一個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名稱同樣的時候,就很差處理,這邊的處理是直接忽略一個,若是有大神有好的方案,歡迎留言,你們一塊兒討論下