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);