wifi扫描并显示
获取列表
获取Wifi列表并不难,网上有一个WifiAdmin的工具类,一找一大堆。但是这个工具类其中还是有很多问题的,并不建议直接使用。在使用过程中还是踩到了其中的一些坑,然后修改一下。
- WiFi名称SSID重复和BSSID
在wifiAdmin中有一个startScan的方法,是获取当前扫描到的所有wifi,另一方法getList就是外部调用获取wifi列表。
SSID就是我们看到的WIFI名,BSSID是在该局域网内唯一的ip。一开始我想是一BSSID做为唯一标识判断重复,但是发现不行。因为如果你的路由器很大或者路由器下面继续接了很多路由器,那么你就会发现通过获取的WifiManager.getScanResults()获取的wifiList中有很多SSID相同,但是BSSID不同的WIFI。那么这些WIFI到底是不是同一个呢,答案是肯定的。然后就得根据SSID进行判重。
/** * 得到网络列表 * * @return the wifi list all */ public ArrayList<ScanResult> getWifiListAll() { ArrayList<ScanResult> newSr = new ArrayList<ScanResult>(); for (ScanResult result : mAllData) { if (!TextUtils.isEmpty(result.SSID) && !result.capabilities.contains("[IBSS]") && !containName(newSr, result)) newSr.add(result); } return newSr; } /** * 判断一个扫描结果中,是否包含了某个名称的WIFI * * @param sr * 扫描结果 * @param scanResult * 要查询的名称 * * @return 返回true表示包含了该名称的WIFI ,返回false表示不包含 */ public boolean containName(List<ScanResult> sr, ScanResult scanResult) { for (ScanResult result : sr) { if (!TextUtils.isEmpty(result.SSID) && result.SSID.equals(scanResult.SSID) && result.capabilities.equals(scanResult.capabilities)) { return true; } } return false; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- WIFI筛选
其实在做判重前可以先做一遍筛选,因为不是所有的WIFI都是我们想要的,我这根据频段做了一次筛选。把startscan方法修改了一下。
/** * 开始扫描 */ public void startScan() { mWifiManager.startScan(); m24GData.clear(); m24GData = null; m24GData = new ArrayList<ScanResult>(); m5GData.clear(); m5GData = null; m5GData = new ArrayList<ScanResult>(); mAllData.clear(); mAllData = null; mAllData = new ArrayList<ScanResult>(); try { List<ScanResult> list = mWifiManager.getScanResults(); if (list != null) { for (ScanResult scanResult : list) { int nSigLevel = WifiManager.calculateSignalLevel( scanResult.level, 100); int value = scanResult.frequency; if (value > 2400 && value < 2500) { m24GData.add(scanResult); } else { m5GData.add(scanResult); } mAllData.add(scanResult); } } else { } } catch (Exception e) { e.printStackTrace(); } mWifiConfiguration = mWifiManager.getConfiguredNetworks(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
WIFI连接
一开始我是使用广播来判断是否连接成功,但要是连接成功还好说,连接不成功我我并不能拿到是哪个未连接成功。后来发现WifiAdmin中也有连接WIFI的方法,试了一下,并不好用。所以重新写了一个连接的方法。原来的方法是直接通过WifiManager.addNetwork()时返回的一个int值返回来判断是否连接成功,但是这个只是是添加成功,并不是连接成功。之后发现WifiManager.enableNetwork()会返回一个boolean值,天真的我以为返回true就是连接成功的意思了,但其实这个值也是不准的。看他的enableNetwork的源码发现有这么一个注释。
/** * Allow a previously configured network to be associated with. If * <code>disableOthers</code> is true, then all other configured * networks are disabled, and an attempt to connect to the selected * network is initiated. This may result in the asynchronous delivery * of state change events. * <p> * <b>Note:</b> If an application's target SDK version is * {@link android.os.Build.VERSION_CODES * communication may not use Wi-Fi even if Wi-Fi is connected; traffic may * instead be sent through another network, such as cellular data, * Bluetooth tethering, or Ethernet. For example, traffic will never use a * Wi-Fi network that does not provide Internet access (e.g. a wireless * printer), if another network that does offer Internet access (e.g. * cellular data) is available. Applications that need to ensure that their * network traffic uses Wi-Fi should use APIs such as * {@link Network * {@link Network * {@link ConnectivityManager * * @param netId the ID of the network in the list of configured networks * @param disableOthers if true, disable all other networks. The way to * select a particular network to connect to is specify {@code true} * for this parameter. * @return {@code true} if the operation succeeded */ public boolean enableNetwork(int netId, boolean disableOthers) { final boolean pin = disableOthers && mTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP; if (pin) { NetworkRequest request = new NetworkRequest.Builder() .clearCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .build(); NetworkPinner.pin(mContext, request); } boolean success; try { success = mService.enableNetwork(netId, disableOthers); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } if (pin && !success) { NetworkPinner.unpin(); } return success; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
所以这个返回值的意思只是有没有去进行连接这个操作,进行连接这个操作了,就返回true,并不是有没有连接成功。值得提一下的是disableOthers这个参数,如果是true则会连的时候将其他的都断开,如果是false,则不会。在使用的时候建议先将所有的断开,这样会比较快一点。
在网上找了好多资料之后还是回到了使用广播判断WIFI是否连接成功。把连接这块单独提了出来做了一个类。在连接前先将需要连接的WIFI传入,然后在连接的时候给他加一把线程锁,设定一个超时时间,等待广播结果。然后再通过接口回调,将连接状态和WIFI信息一并传出。
具体连接的代码就不贴了,太长了。
有几个个问题需要特别注意一下。
我遇到一个4.2.2的系统,他会在连接的时候先将WIFI关闭,然后再开启。这个问题困扰我很长时间,后来找到了需要提高所需连接WIFI的优先级。
cfg.networkId = mNetworkID int newPri = wifiAdmin.getMaxPriority() + 1 cfg.priority = newPri mWifiManager.updateNetwork(cfg) mWifiManager.saveConfiguration()
还有一个就是当连接一个之前连接过的,但是密码已经修改了,需要把原来的移除掉,然后再添加上,不然在设置页面进行忘记密码的时候会需要忘记好几次才能完全忘记。
/** * Remove exit config. * 移除已经连接过的 * * @param ssid * the ssid */ public void removeExitConfig(String ssid) { WifiConfiguration exsits = isExsits(ssid); if (exsits != null) { mWifiManager.removeNetwork(exsits.networkId); } }
/** * Is exsits wifi configuration. * 判断是否已经连接过 * * @param SSID * the ssid * * @return the wifi configuration */ public WifiConfiguration isExsits(String SSID) { List<WifiConfiguration> existingConfigs = mWifiManager.getConfiguredNetworks(); for (WifiConfiguration existingConfig : existingConfigs) { if (existingConfig.SSID.equals("\"" + SSID + "\"")) { return existingConfig; } } return null; }