Android中WiFi模塊在應用層的開發接口以及使用方法android
Android WiFi的掃描、鏈接、信息、以及WiFi熱點等等的實現git
目前,常見及須要處理的熱點,包括如下3大類:github
wps(wifi protected setup):是爲了進一步加強wpa熱點及簡化鏈接過程的技術,不屬於加密類型。api
1 wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
5.0系統的開啓熱點有問題,使用華爲p9Android6.0手機測試確實開啓不了熱點,須要添加write_settings,添加上此權限就能夠成功開啓了。瀏覽器
1 // true表示打開wifi開關,false表示關閉; 2 // 該方法的返回值僅表明操做是否成功,不表明wifi狀態的變化; 3 wifiManager.setWifiEnabled(true);
經過監聽廣播WifiManager.WIFI_STATE_CHANGED_ACTION ,來判斷真正的wifi開關變化,該廣播帶有一個int型的值來表示wifi狀態:安全
1 // 能夠看到,該操做實際上是一個異步操做,通常耗時在1~3秒之間。 2 int wifistate = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED); 4 switch (wifistate) { 5 case WifiManager.WIFI_STATE_DISABLED: 6 //wifi已關閉 7 break; 8 case WifiManager.WIFI_STATE_ENABLED: 9 //wifi已打開 10 break; 11 case WifiManager.WIFI_STATE_ENABLING: 12 //wifi正在打開 13 break; 14 default: 15 break; 16 }
1 // 開始掃描的接口,其返回值表明操做是否成功 2 wifiManager.startScan();
掃描結果:廣播通知:網絡
1 // 獲取掃描結果 2 List<ScanResult> results = wifiManager.getScanResults(); 3 4 // 通常在主動調用startScan以後,大概2秒左右 5 // 會收到WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)廣播通知,該廣播包括一個boolean型的額外參數: 6 7 boolean isScanned = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true); 8 9 // 上面的值表示,掃描結果是否已可用,若可用,則可使用getScanResults獲取結果,在結果沒有就緒以前,會返回null。
通常系統自己會調用startScan接口,而該操做相對比較耗電,所以在應用中要酌情使用,並不須要頻繁調用。異步
獲取wifi熱點:測試
1 public List<AccessPoint> getWiFiList() { 2 List<ScanResult> results = wifiManager.getScanResults(); 3 List<AccessPoint> aps = new ArrayList<AccessPoint>(); 4 DecimalFormat df = new DecimalFormat("#.##"); 5 for (ScanResult result : results) { 6 if (TextUtils.isEmpty(result.SSID)) { 7 continue; 8 } 9 10 AccessPoint accessPoint = new AccessPoint(); 11 accessPoint.setSsid(result.SSID); 12 accessPoint.setBssid(result.BSSID); 13 accessPoint.setEncryptionType(result.capabilities); 14 try { 15 double level = calculateSignalLevel(result.level, 5.0f) / 5.0; 16 level = Double.parseDouble(df.format(level)); 17 accessPoint.setSignalStrength((float) level * 100); 18 } catch (Exception e) { 19 e.printStackTrace(); 20 } 21 int networkId = isConfigured(accessPoint); 22 if (networkId > -1) { 23 accessPoint.setNetworkId(networkId); 24 } 25 aps.add(accessPoint); 26 } 27 return aps; 28 } 29 30 public int isConfigured(AccessPoint ap) { 31 List<WifiConfiguration> configurations = wifiManager.getConfiguredNetworks(); 32 if (configurations == null || configurations.size() <= 0) { 33 Log.d("WIFIX","Config Aps are empty"); 34 return -1; 35 } 36 37 for (WifiConfiguration configuration : configurations) { 38 /** 39 * ssid in WifiConfiguration is always like "CCMC",and bssid is always null 40 */ 41 if (configuration.SSID.replace("\"","").trim().equals(ap.getSsid())) { 42 return configuration.networkId; 43 } 44 } 45 return -1; 46 }
這個類主要是經過Wifi硬件的掃描來獲取一些周邊的wifi熱點(access point)的信息。該類主要有5個字段,this
1 // 全部已經鏈接過的熱點,都會存在本地一個文件中,通常路徑爲/data/misc/wifi/wpa_supplicant.conf(查看需root),而在程序中獲取則經過如下接口: 2 3 List<WifiConfiguration> configurations = wifiManager.getConfiguredNetworks();
獲取到的WiFiConfiguration對象中,只有ssid和networkId是必定有的,能夠用於直接鏈接該熱點,其餘信息如bssid,密鑰等信息基本都是空的。
1 // 該對象表明當前已鏈接的熱點,信息,無鏈接時返回null; 2 // 該對象可獲取包括ssid,bssid,networkId等信息; 3 // 而ssid是包括了雙引號的,如「CCMC」,在以前的掃描結果ScanResult中,ssid並不帶雙引號。 4 5 WifiInfo info = wifiManager.getConnectionInfo();
鏈接一個未鏈接過的熱點時,需3步:
1)建立一個配置:WifiConfiguration
經過該類獲取一個wifi網絡的網絡配置,包括安全配置等。它包含6個子類,以下所示:
建立:
1 public WifiConfiguration createConfiguration(AccessPoint ap) { 2 String SSID = ap.getSsid(); 3 WifiConfiguration config = new WifiConfiguration(); 4 config.SSID = "\"" + SSID + "\""; 5 6 String encryptionType = ap.getEncryptionType(); 7 String password = ap.getPassword(); 8 if (encryptionType.contains("nopass")) { 9 config.wepKeys[0] = ""; 10 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 11 config.wepTxKeyIndex = 0; 12 13 } else if (encryptionType.contains("wep")) { 14 /** 15 * special handling according to password length is a must for wep 16 */ 17 int i = password.length(); 18 if (((i == 10 || (i == 26) || (i == 58))) && (password.matches("[0-9A-Fa-f]*"))) { 19 config.wepKeys[0] = password; 20 } else { 21 config.wepKeys[0] = "\"" + password + "\""; 22 } 23 config.allowedAuthAlgorithms 24 .set(WifiConfiguration.AuthAlgorithm.SHARED); 25 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); 26 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 27 config.wepTxKeyIndex = 0; 28 } else if (encryptionType.contains("wpa")) { 29 config.preSharedKey = "\"" + password + "\""; 30 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); 31 } else { 32 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 33 } 34 return config; 35 }
2)生成一個networkId
1 WifiConfiguration config = createConfiguration(ap); 2 3 /** 4 * 通常狀況下,對一個已經鏈接過的熱點(本地有鏈接記錄),進行addNetwork操做時,在api21及以上會返回一個小於0的networkId。
此時,進行下一步鏈接是沒有意義的,得到一個小於0的networkId已經表示鏈接失敗。 5 */ 6 int networkId = networkId = wifiManager.addNetwork(config);
3)開始鏈接
1 wifiManager.enableNetwork(networkId, true);
對於已鏈接過的熱點:
1 // 獲取已鏈接過的熱點, 獲取到該熱點的networkId以後,可直接進行鏈接 2 List<WifiConfiguration> configurations = wifiManager.getConfiguredNetworks();
注意:
如嘗試一個新密碼,由於即便使用了錯誤的密碼鏈接,系統仍是會爲本次鏈接生成一個本地記錄,則必須在一開始,將本地記錄remove掉
***鏈接結果經過兩個廣播反饋:WifiManager.NETWORK_STATE_CHANGED_ACTION和WifiManager.SUPPLICANT_STATE_CHANGED_ACTION
其中,密碼錯誤的結果通知需經過第二個廣播判斷:
1 int error = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, 0); 2 if (WifiManager.ERROR_AUTHENTICATING == error) { 3 //密碼錯誤,認證失敗 4 }
其餘結果均經過第一個廣播接收:
1 if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 2 NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); 3 if (null != info) { 4 NetworkInfo.DetailedState state = info.getDetailedState(); 5 } 6 }
DetailedState系統定義:
android 系統把CONNECTING,AUTHENTICATING,OBTAINING_IPADDR都規爲CONNECTING
1 /* 2 * IDLE:空閒 3 SCANNING:正在掃描 4 CONNECTING:鏈接中 5 AUTHENTICATING:正在進行身份驗證... 6 OBTAINING_IPADDR:正在獲取Ip地址 7 CONNECTED:已鏈接 8 SUSPENDED:已暫停 9 DISCONNECTING:正在斷開鏈接... 10 DISCONNECTED:已斷開 11 FAILED:失敗 12 BLOCKED:已阻止 13 VERIFYING_POOR_LINK:暫時關閉(網絡情況不佳) 14 CAPTIVE_PORTAL_CHECK:判斷是否須要瀏覽器二次登陸(本人用6.0手機試了,好像不會走到這一步) 15 */ 16 17 public enum DetailedState { 18 /** Ready to start data connection setup. */ 19 IDLE, 20 /** Searching for an available access point. */ 21 SCANNING, 22 /** Currently setting up data connection. */ 23 CONNECTING, 24 /** Network link established, performing authentication. */ 25 AUTHENTICATING, 26 /** Awaiting response from DHCP server in order to assign IP address information. */ 27 OBTAINING_IPADDR, 28 /** IP traffic should be available. */ 29 CONNECTED, 30 /** IP traffic is suspended */ 31 SUSPENDED, 32 /** Currently tearing down data connection. */ 33 DISCONNECTING, 34 /** IP traffic not available. */ 35 DISCONNECTED, 36 /** Attempt to connect failed. */ 37 FAILED, 38 /** Access to this network is blocked. */ 39 BLOCKED, 40 /** Link has poor connectivity. */ 41 VERIFYING_POOR_LINK, 42 /** Checking if network is a captive portal */ 43 CAPTIVE_PORTAL_CHECK 44 }
1 // 方法返回值表明當前操做是否成功,操做的最終結果,會在兩個廣播中有所反饋: 2 // (1)WifiManager.SUPPLICANT_STATE_CHANGED_ACTION 3 // (2)WifiManager.NETWORK_STATE_CHANGED_ACTION 4 5 // 而且斷開成功的廣播會發送若干次。 6 7 wifiManager.disconnect();
1 // 返回值表明操做是否成功,該操做在api21以上的系統中,成功率在10%如下,在api21如下,基本均可以成功; 2 // 能夠經過反覆進行此操做來提升成功率,但效果不大。 3 boolean isRemoved = wifiManager.removeNetwork(networkId);