一、获取 WIFI 列表
1. 开启 WIFI
1.1 添加权限
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
1.2 使用 WifiManager 开启 Wifi
通过 Application 的 Context 获取到 WifiManager,调用 setWifiEnable(true) 开启 wifi。
public class MainActivity extends AppCompatActivity {
private WifiManager wifiManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(true);
}
}
2. 扫描 WIFI 并获取结果
2.1 添加权限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
2.2 开始扫描
使用 startScan() 方法开始扫描附近的 WIFI 信号。
wifiManager.startScan();
2.3 获取扫描结果
启动扫描后并不能立即获取到扫描结果,系统在扫描结果准备好之后会发送 WifiManager.SCAN_RESULTS_AVAILABLE_ACTION 广播,可以通过监听该广播,并在收到广播后调用 getScanResults() 获取到扫描结果。
注意:getScanResults() 在 Android 6.0 以上的设备返回为空列表的解决办法。
- 将 targetSdkVersion 设置为 22 即可获取到有效结果。
- 添加 ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION 权限并且开启 GPS 定位,才能获取到有效结果。
@Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
registerReceiver(broadcastReceiver, filter);
}
@Override
protected void onStop() {
super.onStop();
unregisterReceiver(broadcastReceiver);
}
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
List<ScanResult> scanResults = wifiManager.getScanResults();
}
};
ScanResult 中保存了 WIFI 的名称、加密方式、信号强度等信息。
3. 获取已保存的 WIFI 列表
使用 WifiManager 的 getConfiguredNetworks() 方法获取设备中已保存的 WIFI 列表的配置信息。
List<WifiConfiguration> wifiConfigurations = WifiUtils.getInstance().getConfiguredNetworks();
WifiConfiguration 中除了有 WIFI 的名称、加密方式外,还有 networkId、状态及相关配置信息。
4. 获取当前连接的 WIFI 信息和状态
通过 ConnectivityManager 的 getActiveNetworkInfo() 方法获取到当前连接的网络信息;通过 WifiManager 的 getConnectionInfo() 方法获取当前连接的 WIFI 信息;根据当前 WIFI 的 networkId 获取到当前 WIFI 的配置和状态。
private NetworkInfo lastNetworkInfo;
private WifiInfo lastWifiInfo;
private WifiConfiguration lastWifiConfiguration;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
lastNetworkInfo = getActiveNetworkInfo();
lastWifiInfo = wifiManager.getConnectionInfo();
if (lastWifiInfo != null && lastWifiInfo.getNetworkId() != -1) {
lastWifiConfiguration = getWifiConfigurationForNetworkId(lastWifiInfo.getNetworkId());
}
}
/**
* 根据 NetworkId 获取 WifiConfiguration 信息
*
* @param networkId 需要获取 WifiConfiguration 信息的 networkId
* @return 指定 networkId 的 WifiConfiguration 信息
*/
private WifiConfiguration getWifiConfigurationForNetworkId(int networkId) {
final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks();
if (configs != null) {
for (WifiConfiguration config : configs) {
if (lastWifiInfo != null && networkId == config.networkId) {
return config;
}
}
}
return null;
}
/**
* 获取当前网络信息
*/
public NetworkInfo getActiveNetworkInfo() {
ConnectivityManager cm = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
return cm.getActiveNetworkInfo();
}
return null;
}
5. 创建 AccessPoint 来保存 WIFI 的信息和状态
分别获取到附近的 WIFI 列表和已保存的 WIFI 列表后,需要匹配附近的 WIFI 和已保存的 WIFI,对已匹配上的 WIFI 需要同事保存其信息和状态。通过查看源码可以知道,Settings 中 WIFI 设置的列表是将 WIFI 信息和状态保存到了 AccessPoint 中。但是 AccessPoint 无法直接使用,所以需要自己创建一个 AccessPoint 类。
通过 ScanResult 列表和 WifiConfiguration 列表得到 AccessPoint 列表
private NetworkInfo lastNetworkInfo;
private WifiInfo lastWifiInfo;
private WifiConfiguration lastWifiConfiguration;
private List<AccessPoint> lastAccessPoints = new CopyOnWriteArrayList<>();
private void updateAccessPoints() {
Single.create((SingleOnSubscribe<List<AccessPoint>>) emitter -> {
List<AccessPoint> accessPoints = new ArrayList<>();
List<ScanResult> scanResults = wifiManager.getScanResults();
if (lastWifiInfo != null && lastWifiInfo.getNetworkId() != AccessPoint.INVALID_NETWORK_ID) {
lastWifiConfiguration = getWifiConfigurationForNetworkId(lastWifiInfo.getNetworkId());
}
if (scanResults != null) {
for (ScanResult scanResult : scanResults) {
if (TextUtils.isEmpty(scanResult.SSID)) {
continue;
}
AccessPoint accessPoint = new AccessPoint(this.getApplicationContext(), scanResult);
if (accessPoints.contains(accessPoint)) {
continue;
}
List<WifiConfiguration> wifiConfigurations = wifiManager.getConfiguredNetworks();
if (wifiConfigurations != null) {
for (WifiConfiguration config : wifiConfigurations) {
if (accessPoint.getQuotedSSID().equals(config.SSID)) {
accessPoint.setWifiConfiguration(config);
}
}
}
if (lastWifiInfo != null && lastNetworkInfo != null) {
accessPoint.update(lastWifiConfiguration, lastWifiInfo, lastNetworkInfo);
}
accessPoints.add(accessPoint);
}
}
Collections.sort(accessPoints);
emitter.onSuccess(accessPoints);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<List<AccessPoint>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onSuccess(List<AccessPoint> accessPoints) {
// 在界面上展示
}
@Override
public void onError(Throwable e) {
}
});
}
二、WIFI 列表刷新
获取到 WIFI 列表后,需要定时刷新,并且在 WIFI 连接发生变化时也需要刷新列表
1. 定时刷新
每隔 10 秒钟调用一次 startScan() 方法,然后在广播接收者中更新列表
private Disposable scanWifi;
@Override
protected void onStart() {
super.onStart();
scanWifi = Observable.interval(0, 10, TimeUnit.SECONDS)
.observeOn(Schedulers.io())
.doOnNext(aLong -> wifiManager.startScan())
.subscribe();
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
registerReceiver(broadcastReceiver, filter);
}
@Override
protected void onStop() {
super.onStop();
if (scanWifi != null && scanWifi.isDisposed()) {
scanWifi.dispose();
}
unregisterReceiver(broadcastReceiver);
}
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateAccessPoints();
}
};
2. 监听网络变化
网络变化的广播:
-
ConnectivityManager.CONNECTIVITY_ACTION
"android.net.conn.CONNECTIVITY_CHANGE",网络连接发生变化时的广播,如:建立连接或断开连接等
-
WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION
"android.net.wifi.CONFIGURED_NETWORKS_CHANGE",已配置的 WIFI 网络发生变化时的广播,如:添加,更新或删除等
-
WifiManager.LINK_CONFIGURATION_CHANGED_ACTION
"android.net.wifi.LINK_CONFIGURATION_CHANGED",WIFI 链接配置发生变化时的广播
-
WifiManager.NETWORK_STATE_CHANGED_ACTION
"android.net.wifi.STATE_CHANGE",WIFI 的连接状态发生变化时的广播
-
WifiManager.SUPPLICANT_STATE_CHANGED_ACTION
"android.net.wifi.supplicant.STATE_CHANGE",与 WIFI 建立连接的状态发生变化时的广播,如:密码错误等
注册广播
@Override
protected void onStart() {
super.onStart();
...
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
filter.addAction("android.net.wifi.CONFIGURED_NETWORKS_CHANGE");
filter.addAction("android.net.wifi.LINK_CONFIGURATION_CHANGED");
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
registerReceiver(broadcastReceiver, filter);
}
接收广播并更新 wifi 列表
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null) {
switch (action) {
case ConnectivityManager.CONNECTIVITY_ACTION:
case WifiManager.SCAN_RESULTS_AVAILABLE_ACTION:
case "android.net.wifi.CONFIGURED_NETWORKS_CHANGE":
case "android.net.wifi.LINK_CONFIGURATION_CHANGED":
updateAccessPoints();
break;
case WifiManager.NETWORK_STATE_CHANGED_ACTION:
updateAccessPoints();
NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
updateNetworkInfo(info);
break;
case WifiManager.SUPPLICANT_STATE_CHANGED_ACTION:
int error = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
if (error == WifiManager.ERROR_AUTHENTICATING) {
// 处理密码错误
handlePasswordError();
}
break;
}
}
}
};
注册网络回调
@Override
protected void onStart() {
super.onStart();
...
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
// 只关心 WIFI
NetworkRequest.Builder request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
cm.registerNetworkCallback(request.build(), callback);
}
}
@Override
protected void onStop() {
super.onStop();
...
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
cm.unregisterNetworkCallback(callback);
}
}
private ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback() {
@Override
// 网络可用时
public void onAvailable(Network network) {
super.onAvailable(network);
setCurrentNetwork(network);
portalCurrentWifi();
}
@Override
// 联网能力发生变化时
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
super.onCapabilitiesChanged(network, networkCapabilities);
if (network.equals(getCurrentNetwork())) {
updateNetworkInfo(null);
}
}
};
更新当前连接 wifi 的状态
private NetworkInfo lastNetworkInfo;
private WifiInfo lastWifiInfo;
private WifiConfiguration lastWifiConfiguration;
private List<AccessPoint> lastAccessPoints = new CopyOnWriteArrayList<>();
private int lastPortalNetworkId = AccessPoint.INVALID_NETWORK_ID;
public void updateNetworkInfo(NetworkInfo networkInfo) {
Single.create((SingleOnSubscribe<List<AccessPoint>>) emitter -> {
if (networkInfo != null) {
lastNetworkInfo = networkInfo;
}
lastWifiInfo = wifiManager.getConnectionInfo();
if (lastWifiInfo.getNetworkId() == AccessPoint.INVALID_NETWORK_ID) {
// 表示没有 wifi 连接,lastPortalNetworkId 置为无效
lastPortalNetworkId = AccessPoint.INVALID_NETWORK_ID;
}
if (lastWifiInfo != null && lastWifiInfo.getNetworkId() != AccessPoint.INVALID_NETWORK_ID) {
lastWifiConfiguration = getWifiConfigurationForNetworkId(lastWifiInfo.getNetworkId());
}
boolean reorder = false;
for (AccessPoint accessPoint : lastAccessPoints) {
if (accessPoint.update(lastWifiConfiguration, lastWifiInfo, lastNetworkInfo)) {
reorder = true;
}
}
if (reorder) {
Collections.sort(lastAccessPoints);
}
emitter.onSuccess(lastAccessPoints);
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<List<AccessPoint>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onSuccess(List<AccessPoint> accessPoints) {
// 更新列表
adapter.setAccessPoints(accessPoints);
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
});
}
三、WIFI 连接
需要用到的 API
- addNetwork(WifiConfiguration config)
- enableNetwork(int netId, boolean attemptConnect)
private void connect(AccessPoint accessPoint) {
// 创建 WifiConfiguration
accessPoint.generateNetworkConfig();
// 添加 WifiConfiguration
int networkId = wifiManager.addNetwork(accessPoint.wifiConfiguration);
// 启用并尝试连接到 wifi
wifiManager.enableNetwork(networkId, true);
}
/**
* 生成 wifiConfiguration
*/
public void generateNetworkConfig() {
if (wifiConfiguration != null)
return;
wifiConfiguration = new WifiConfiguration();
wifiConfiguration.SSID = getQuotedSSID();
switch (security) {
case SECURITY_NONE:
wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
break;
case SECURITY_WEP:
wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
wifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
wifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
int length = password.length();
if ((length == 10 || length == 26 || length == 58) &&
password.matches("[0-9A-Fa-f]*")) {
wifiConfiguration.wepKeys[0] = password;
} else {
wifiConfiguration.wepKeys[0] = '"' + password + '"';
}
break;
case SECURITY_PSK:
wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
if (password.matches("[0-9A-Fa-f]{64}")) {
wifiConfiguration.preSharedKey = password;
} else {
wifiConfiguration.preSharedKey = '"' + password + '"';
}
break;
case SECURITY_EAP:
// 暂时忽略
break;
}
}
判断 wifi 是否需要登录
网络可用时,连接指定网址根据返回码是否是 204 判断 wifi 是否需要登录
private ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback() {
@Override
// 网络可用时
public void onAvailable(Network network) {
super.onAvailable(network);
setCurrentNetwork(network);
portalCurrentWifi();
}
};
public void portalCurrentWifi() {
if (lastWifiInfo.getNetworkId() != lastPortalNetworkId) {
lastPortalNetworkId = lastWifiInfo.getNetworkId();
Single.create((SingleOnSubscribe<Boolean>) emitter -> {
Network currentNetwork = getCurrentNetwork();
HttpURLConnection urlConnection = null;
try {
// 使用当前的网络打开链接
urlConnection = (HttpURLConnection) currentNetwork.openConnection(new URL("http://connect.rom.miui.com/generate_204"));
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(10000);
urlConnection.setReadTimeout(10000);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
int responseCode = urlConnection.getResponseCode();
if (responseCode == 200 && urlConnection.getContentLength() == 0) {
responseCode = 204;
}
emitter.onSuccess(responseCode != 204 && responseCode >= 200 && responseCode <= 399);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
})
.retry(throwable -> throwable instanceof UnknownHostException)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Boolean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onSuccess(Boolean aBoolean) {
if (aBoolean) {
// 调用网络登录界面
}
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
});
}
}
四、取消保存
需要用到的 API
- removeNetwork(int netId)
public void forgetWifi(AccessPoint accessPoint) {
wifiManager.removeNetwork(accessPoint.wifiConfiguration.networkId);
}
PS 权限问题:对于普通应用只能取消保存自己添加的 wifi,无法取消保存其它应用添加的 wifi;如果需要取消保存其它应用添加的 wifi,需要添加如下权限,并作为系统应用。
<!--覆盖wifi配置 需要是 system app-->
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />