Android WiFi 開發

Android中WiFi模塊在應用層的開發接口以及使用方法android

Android WiFi的掃描、鏈接、信息、以及WiFi熱點等等的實現git

涉及到的類
  • WifiManager ——入口類,Wifi相關的全部操做均經過此類
  • WifiConfiguration——進行熱點鏈接時,經過該類爲熱點建立一個配置,並由WifiManager以此配置生成一個networkId,後開始鏈接;此外,也用於表示一個已鏈接的熱點在本地的記錄
  • WifiInfo——表示當前的wifi網絡鏈接信息
  • ScanResult——掃描到的熱點信息類,每個對象表明一個掃描到的熱點,其中包括若干該熱點信息
涉及到的廣播
  • WifiManager.WIFI_STATE_CHANGED_ACTION ——wifi開關變化廣播
  • WifiManager.SCAN_RESULTS_AVAILABLE_ACTION——熱點掃描結果通知廣播
  • WifiManager.SUPPLICANT_STATE_CHANGED_ACTION——熱點鏈接結果通知廣播
  • WifiManager.NETWORK_STATE_CHANGED_ACTION——網絡狀態變化廣播(與上一廣播協同完成鏈接過程通知)

相關屬性及概念

  • networkId——鏈接某個wifi熱點時,系統會爲該熱點生成一個networkId,在同一設備上,不一樣熱點的networkId是惟一的,一般狀況下爲大於0的整數,在某些設備上,恢復出廠後鏈接的第一個熱點networkId爲0
  • ssid——wifi熱點名稱,可重複
  • bssid——相似於mac地址,但並非路由器的mac地址,與ssid一塊兒可做爲熱點的惟一標識,同時該屬性每一個熱點惟一不重複
  • 親屬熱點——(本文設定概念)ssid相同,但bssid不一樣的全部熱點,互爲親屬熱點,android設備會將ssid相同的全部親屬熱點當作一個熱點進行處理

熱點加密類型

目前,常見及須要處理的熱點,包括如下3大類:github

  • open——開放型網絡,即無加密,可直接鏈接
  • wep——採用wep加密類型的熱點,已過期,不安全,容易被破解,目前使用率已不足10%
  • wpa/wpa2——目前使用最普遍,相對最安全,破解難度最大的加密類型

wps(wifi protected setup):是爲了進一步加強wpa熱點及簡化鏈接過程的技術,不屬於加密類型。api

開發細節

1 獲取WifiManager入口類實例:

1 wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

2 打開及關閉wifi

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         }

3 周圍熱點掃描

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     }

ScanResult類

這個類主要是經過Wifi硬件的掃描來獲取一些周邊的wifi熱點(access point)的信息。該類主要有5個字段,this

打印信息以下:

 

4 獲取已鏈接過的熱點

1 // 全部已經鏈接過的熱點,都會存在本地一個文件中,通常路徑爲/data/misc/wifi/wpa_supplicant.conf(查看需root),而在程序中獲取則經過如下接口:
2 
3 List<WifiConfiguration> configurations = wifiManager.getConfiguredNetworks();

獲取到的WiFiConfiguration對象中,只有ssid和networkId是必定有的,能夠用於直接鏈接該熱點,其餘信息如bssid,密鑰等信息基本都是空的。

5 獲取當前wifi鏈接信息

1 // 該對象表明當前已鏈接的熱點,信息,無鏈接時返回null; 2 // 該對象可獲取包括ssid,bssid,networkId等信息; 3 // 而ssid是包括了雙引號的,如「CCMC」,在以前的掃描結果ScanResult中,ssid並不帶雙引號。
4 
5 WifiInfo info = wifiManager.getConnectionInfo();

6 鏈接指定熱點

鏈接一個未鏈接過的熱點時,需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     }

7 斷開當前wifi鏈接

1 // 方法返回值表明當前操做是否成功,操做的最終結果,會在兩個廣播中有所反饋: 2 // (1)WifiManager.SUPPLICANT_STATE_CHANGED_ACTION 3 // (2)WifiManager.NETWORK_STATE_CHANGED_ACTION 4 
5 // 而且斷開成功的廣播會發送若干次。
6 
7 wifiManager.disconnect();

8 遺忘一個已鏈接過的熱點

1 // 返回值表明操做是否成功,該操做在api21以上的系統中,成功率在10%如下,在api21如下,基本均可以成功; 2 // 能夠經過反覆進行此操做來提升成功率,但效果不大。
3 boolean isRemoved = wifiManager.removeNetwork(networkId);

 

DEMO: https://github.com/charlesgrant/WIFIDemo 

相關文章
相關標籤/搜索