Wifi开发上层学习1:实现一个wifi搜索以及打开的app
文章目录
- Wifi开发上层学习1:实现一个wifi搜索以及打开的app
- 背景
- demo实现
- 1.添加系统权限以及系统签名
- 2.布局配置
- 3.逻辑设计
- 3.1 wifi开关的实现
- 3.2 wifi扫描功能
- 3.3 连接wifi
- 总结
- 一、WiFi 状态控制接口
- 二、WiFi 扫描相关接口
- 三、WiFi 连接与配置管理接口
- 四、连接状态信息接口
- 关键广播(配合接口使用)
背景
接下来会从上到下贯穿的学习wifi,相机等系统功能。今天是学习wifi的第一天,主要是调用wifiManager的一些api来实现wifi功能的开关,wifi搜索,以及记录和连接。
demo实现
1.添加系统权限以及系统签名
添加权限
<!-- WiFi状态获取 --><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><!-- WiFi状态修改 --><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /><!-- Android 10+ 扫描WiFi需位置权限 --><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><!-- Android 12+ 扫描WiFi需附近WiFi权限(无需定位) --><uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" android:usesPermissionFlags="neverForLocation" />
添加系统签名
具体流程不展示,注意gradle中配置的密码等需要匹配,以及在manifest中配置android:sharedUserId="android.uid.system"
2.布局配置
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"tools:context=".MainActivity"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="@string/app_name"android:textSize="24sp"android:textStyle="bold"android:layout_marginBottom="16dp" /><!-- WiFi开关 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:gravity="center_vertical"android:layout_marginBottom="16dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="WiFi开关:"android:textSize="18sp"/><Switchandroid:id="@+id/wifi_switch"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="16dp"/></LinearLayout><!-- 扫描按钮 --><Buttonandroid:id="@+id/scan_button"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="扫描WiFi网络"android:layout_marginBottom="16dp"/><!-- 扫描结果列表 --><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="可用网络:"android:textSize="18sp"android:layout_marginBottom="8dp"/><ListViewandroid:id="@+id/wifi_list"android:layout_width="match_parent"android:layout_height="match_parent"/></LinearLayout>
主界面布局非常简单一个switch,下面一个扫描按钮,最后的扫描结果使用listView展示
wifi_list_item.layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="16dp"android:gravity="center_vertical"><!-- WiFi名称 + 已连接标识 + 已保存标识 --><LinearLayoutandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:orientation="horizontal"android:gravity="center_vertical"><!-- WiFi名称 --><TextViewandroid:id="@+id/ssid_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="16sp"android:textColor="@android:color/black"/><!-- 已连接标签(必须有@+id/tv_connected) --><TextViewandroid:id="@+id/tv_connected"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="(已连接)"android:textSize="14sp"android:textColor="@android:color/holo_green_dark"android:layout_marginStart="8dp"android:visibility="gone"/><!-- 已保存标签(关键:补全@+id/saved_indicator) --><TextViewandroid:id="@+id/saved_indicator"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="(已保存)"android:textSize="14sp"android:textColor="@android:color/holo_blue_dark"android:layout_marginStart="8dp"android:visibility="gone"/></LinearLayout><!-- 信号强度 --><TextViewandroid:id="@+id/strength_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="14sp"android:textColor="@android:color/darker_gray"android:layout_marginStart="16dp"android:layout_marginEnd="16dp"/><!-- 加密方式 --><TextViewandroid:id="@+id/security_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="14sp"android:textColor="@android:color/darker_gray"/></LinearLayout>
item设计,主要设置两个默认不可见的标签,分别标识已连接的wifi以及已保存的wifi
dialog_connect.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:padding="16dp"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="连接到网络"android:textSize="18sp"android:textStyle="bold"android:layout_marginBottom="16dp" /><TextViewandroid:id="@+id/dialog_ssid"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="16sp"android:layout_marginBottom="16dp" /><EditTextandroid:id="@+id/password_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="输入密码"android:inputType="textPassword"/></LinearLayout>
3.逻辑设计
// 初始化WiFi管理器wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
首先需要拿到wifiManager的实例
3.1 wifi开关的实现
安卓12之后不允许应用修改wifi开关,所以这里必须要系统签名
核心方法
wifiManager.setWifiEnabled();
之后会更新这个逻辑链怎么传到下层
wifiSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {if (isChecked && !wifiManager.isWifiEnabled()) {checkPermissionsAndEnableWifi();} else if (!isChecked && wifiManager.isWifiEnabled()) {wifiManager.setWifiEnabled(false);}});
checkPermissionsAndEnableWifi()
这其中与下面的关闭一样,就是多加个权限检查
更新开关状态
// 更新WiFi开关状态private void updateWifiSwitchState(int state) {Log.d(TAG, "updateWifiSwitchState: " + state);switch (state) {case WifiManager.WIFI_STATE_ENABLED:wifiSwitch.setEnabled(true);wifiSwitch.setChecked(true);scanButton.setEnabled(true);// 刷新当前连接的WiFi信息currentConnectedSSID = getCurrentConnectedWifiSSID();configuredNetworks = wifiManager.getConfiguredNetworks();break;case WifiManager.WIFI_STATE_DISABLED:wifiSwitch.setEnabled(true);wifiSwitch.setChecked(false);scanButton.setEnabled(false);sortedScanResults.clear();adapter.notifyDataSetChanged();break;default:wifiSwitch.setEnabled(false);scanButton.setEnabled(false);break;}}
3.2 wifi扫描功能
wifi搜索的过程可以分为
配置scanButton的点击事件
//onCreate
// 扫描按钮监听scanButton.setOnClickListener(v -> checkPermissionsAndScan());private void checkPermissionsAndScan() {if (hasRequiredPermissions()) {startWifiScan();} else {requestPermissions();}}
//开始扫描private void startWifiScan() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isForeground()) {Toast.makeText(this, "应用需要在前台才能扫描WiFi",Toast.LENGTH_SHORT).show();return;}boolean started = wifiManager.startScan();if (!started) {Toast.makeText(this, "扫描失败,请重试", Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "开始扫描...", Toast.LENGTH_SHORT).show();}}
// 检查应用是否在前台private boolean isForeground() {ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();if (appProcesses == null) {return false;}final String packageName = getPackageName();for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {if (appProcess.processName.equals(packageName) &&appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {return true;}}return false;}
核心方法 wifiManager.startScan()
3.3 连接wifi
1.列表项点击事件
private void handleWifiItemClick(ScanResult result) {// 检查是否是已保存网络if (isWifiSaved(result.SSID)) {// 已保存网络:直接连接connectToSavedWifi(result.SSID);} else {// 未保存网络:显示密码对话框showConnectDialog(result);}}
// 判断WiFi是否已保存private boolean isWifiSaved(String ssid) {if (configuredNetworks == null || configuredNetworks.isEmpty()) {return false;}// 已保存网络的SSID带双引号String targetSSID = "\"" + ssid + "\"";for (WifiConfiguration config : configuredNetworks) {if (targetSSID.equals(config.SSID)) {return true;}}return false;}
对比系统中已保存的 WiFi 配置列表(WifiManager.getConfiguredNetworks()
)与当前 WiFi 的 SSID,若存在匹配项,则视为 “已保存”。
// 连接已保存的WiFi private void connectToSavedWifi(String ssid) {WifiConfiguration savedConfig = getSavedWifiConfig(ssid);if (savedConfig != null) {// 检查是否已是当前连接的网络if (ssid.equals(currentConnectedSSID)) {Toast.makeText(this, "已是当前连接的网络:" + ssid, Toast.LENGTH_SHORT).show();return;}// 断开当前连接,连接选中的已保存网络wifiManager.disconnect();boolean isConnected = wifiManager.enableNetwork(savedConfig.networkId, true);wifiManager.reconnect();if (isConnected) {Toast.makeText(this, "正在连接:" + ssid, Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "连接失败,请重试", Toast.LENGTH_SHORT).show();}} else {// 理论上不会走到这里,因为已做过isWifiSaved判断Toast.makeText(this, "网络配置不存在", Toast.LENGTH_SHORT).show();}}
wifiManager.disconnect(); boolean isConnected = wifiManager.enableNetwork(savedConfig.networkId, true); wifiManager.reconnect();
//核心逻辑
//未保存的wifi,打开dialog
private void showConnectDialog(ScanResult scanResult) {AlertDialog.Builder builder = new AlertDialog.Builder(this);LayoutInflater inflater = getLayoutInflater();View dialogView = inflater.inflate(R.layout.dialog_connect, null);builder.setView(dialogView);TextView ssidText = dialogView.findViewById(R.id.dialog_ssid);EditText passwordInput = dialogView.findViewById(R.id.password_input);ssidText.setText(scanResult.SSID);// 开放网络隐藏密码输入框if (!scanResult.capabilities.contains("WEP") && !scanResult.capabilities.contains("WPA")) {passwordInput.setVisibility(View.GONE);}builder.setPositiveButton("连接", (dialog, which) -> {String password = passwordInput.getText().toString();connect(scanResult.SSID, password, scanResult.capabilities);});builder.setNegativeButton("取消", null);builder.show();}
连接wifi的核心方法
// 连接WiFi的核心方法private void connect(String ssid, String password, String capabilities) {// 先检查是否已保存该WiFi(防止重复保存)WifiConfiguration savedConfig = getSavedWifiConfig(ssid);if (savedConfig != null) {connectToSavedWifi(ssid);return;}// 未保存:创建新配置并保存WifiConfiguration newConfig = new WifiConfiguration();newConfig.SSID = "\"" + ssid + "\"";// 根据加密类型设置配置if (capabilities.contains("WEP")) {newConfig.wepKeys[0] = "\"" + password + "\"";newConfig.wepTxKeyIndex = 0;newConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);newConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);} else if (capabilities.contains("WPA")) {newConfig.preSharedKey = "\"" + password + "\"";} else {// 开放网络newConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);}// 添加配置并连接int networkId = wifiManager.addNetwork(newConfig);if (networkId != -1) {// 保存配置wifiManager.saveConfiguration();// 启用该网络并断开其他网络wifiManager.disconnect();boolean isConnected = wifiManager.enableNetwork(networkId, true);wifiManager.reconnect();if (isConnected) {Toast.makeText(this, "正在连接:" + ssid, Toast.LENGTH_SHORT).show();// 更新已保存列表configuredNetworks = wifiManager.getConfiguredNetworks();} else {// 连接失败:删除无效配置wifiManager.removeNetwork(networkId);wifiManager.saveConfiguration();Toast.makeText(this, "连接失败,请检查密码", Toast.LENGTH_SHORT).show();}} else {Toast.makeText(this, "无法添加网络配置", Toast.LENGTH_SHORT).show();}}
总结
完整代码
package com.example.wifitest;import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class MainActivity extends AppCompatActivity {private static final int PERMISSION_REQUEST_CODE = 100;public static final String TAG = "WifiTest";private WifiManager wifiManager;private Switch wifiSwitch;private Button scanButton;private ListView wifiList;private WifiListAdapter adapter;// 当前连接的WiFi SSID(不带双引号)private String currentConnectedSSID;// 排序后的WiFi列表private List<ScanResult> sortedScanResults;// 已保存的WiFi配置列表private List<WifiConfiguration> configuredNetworks;// WiFi状态广播接收器private BroadcastReceiver wifiStateReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);updateWifiSwitchState(state);}};// WiFi扫描结果广播接收器(修改:确保排序正确)private BroadcastReceiver scanResultsReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {boolean success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);if (success && wifiManager.isWifiEnabled()) {List<ScanResult> tempResults = wifiManager.getScanResults();// 步骤1:去重(同一SSID保留信号最强的)List<ScanResult> uniqueResults = removeDuplicateSSID(tempResults);// 步骤2:按规则排序(当前连接置顶)sortWifiList(uniqueResults);// 步骤3:更新列表adapter.notifyDataSetChanged();Toast.makeText(MainActivity.this,"扫描完成,发现 " + sortedScanResults.size() + " 个网络",Toast.LENGTH_SHORT).show();}}};// 监听WiFi连接状态变化的广播接收器(新增)private BroadcastReceiver connectionStateReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {// 当WiFi连接状态变化时,更新当前连接信息并重新排序currentConnectedSSID = getCurrentConnectedWifiSSID();configuredNetworks = wifiManager.getConfiguredNetworks();if (sortedScanResults != null && !sortedScanResults.isEmpty()) {sortWifiList(sortedScanResults);adapter.notifyDataSetChanged();}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化WiFi管理器wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);// 初始化视图wifiSwitch = findViewById(R.id.wifi_switch);scanButton = findViewById(R.id.scan_button);wifiList = findViewById(R.id.wifi_list);// 初始化成员变量currentConnectedSSID = getCurrentConnectedWifiSSID();configuredNetworks = wifiManager.getConfiguredNetworks();sortedScanResults = new ArrayList<>();// 初始化适配器adapter = new WifiListAdapter();wifiList.setAdapter(adapter);// 注册广播接收器(新增连接状态变化接收器)registerReceiver(wifiStateReceiver, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));registerReceiver(scanResultsReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));registerReceiver(connectionStateReceiver, new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION));// WiFi开关监听wifiSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {if (isChecked && !wifiManager.isWifiEnabled()) {checkPermissionsAndEnableWifi();} else if (!isChecked && wifiManager.isWifiEnabled()) {wifiManager.setWifiEnabled(false);}});// 扫描按钮监听scanButton.setOnClickListener(v -> checkPermissionsAndScan());// 列表项点击事件(修改:区分已保存和未保存网络)wifiList.setOnItemClickListener((parent, view, position, id) -> {if (sortedScanResults != null && position < sortedScanResults.size()) {ScanResult result = sortedScanResults.get(position);handleWifiItemClick(result);}});// 初始状态更新updateWifiSwitchState(wifiManager.getWifiState());}@Overrideprotected void onDestroy() {super.onDestroy();unregisterReceiver(wifiStateReceiver);unregisterReceiver(scanResultsReceiver);unregisterReceiver(connectionStateReceiver); // 注销新增的接收器}// 更新WiFi开关状态private void updateWifiSwitchState(int state) {Log.d(TAG, "updateWifiSwitchState: " + state);switch (state) {case WifiManager.WIFI_STATE_ENABLED:wifiSwitch.setEnabled(true);wifiSwitch.setChecked(true);scanButton.setEnabled(true);// 刷新当前连接的WiFi信息currentConnectedSSID = getCurrentConnectedWifiSSID();configuredNetworks = wifiManager.getConfiguredNetworks();break;case WifiManager.WIFI_STATE_DISABLED:wifiSwitch.setEnabled(true);wifiSwitch.setChecked(false);scanButton.setEnabled(false);sortedScanResults.clear();adapter.notifyDataSetChanged();break;default:wifiSwitch.setEnabled(false);scanButton.setEnabled(false);break;}}// 处理WiFi列表项点击(新增:区分已保存和未保存网络)private void handleWifiItemClick(ScanResult result) {// 检查是否是已保存网络if (isWifiSaved(result.SSID)) {// 已保存网络:直接连接connectToSavedWifi(result.SSID);} else {// 未保存网络:显示密码对话框showConnectDialog(result);}}// 显示连接对话框(提取为独立方法)private void showConnectDialog(ScanResult scanResult) {AlertDialog.Builder builder = new AlertDialog.Builder(this);LayoutInflater inflater = getLayoutInflater();View dialogView = inflater.inflate(R.layout.dialog_connect, null);builder.setView(dialogView);TextView ssidText = dialogView.findViewById(R.id.dialog_ssid);EditText passwordInput = dialogView.findViewById(R.id.password_input);ssidText.setText(scanResult.SSID);// 开放网络隐藏密码输入框if (!scanResult.capabilities.contains("WEP") && !scanResult.capabilities.contains("WPA")) {passwordInput.setVisibility(View.GONE);}builder.setPositiveButton("连接", (dialog, which) -> {String password = passwordInput.getText().toString();connect(scanResult.SSID, password, scanResult.capabilities);});builder.setNegativeButton("取消", null);builder.show();}// 连接已保存的WiFi(新增)private void connectToSavedWifi(String ssid) {WifiConfiguration savedConfig = getSavedWifiConfig(ssid);if (savedConfig != null) {// 检查是否已是当前连接的网络if (ssid.equals(currentConnectedSSID)) {Toast.makeText(this, "已是当前连接的网络:" + ssid, Toast.LENGTH_SHORT).show();return;}// 断开当前连接,连接选中的已保存网络wifiManager.disconnect();boolean isConnected = wifiManager.enableNetwork(savedConfig.networkId, true);wifiManager.reconnect();if (isConnected) {Toast.makeText(this, "正在连接:" + ssid, Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "连接失败,请重试", Toast.LENGTH_SHORT).show();}} else {// 理论上不会走到这里,因为已做过isWifiSaved判断Toast.makeText(this, "网络配置不存在", Toast.LENGTH_SHORT).show();}}// 获取当前连接的WiFi SSIDprivate String getCurrentConnectedWifiSSID() {if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_WIFI_STATE) != PackageManager.PERMISSION_GRANTED) {return null;}WifiInfo wifiInfo = wifiManager.getConnectionInfo();// 判断是否真的连接了WiFi(networkId != -1 表示已连接)if (wifiInfo != null && wifiInfo.getNetworkId() != -1) {String ssid = wifiInfo.getSSID();// 去掉SSID前后的双引号if (ssid.startsWith("\"") && ssid.endsWith("\"")) {ssid = ssid.substring(1, ssid.length() - 1);}return ssid;}return null;}// 去重:同一SSID只保留信号最强的private List<ScanResult> removeDuplicateSSID(List<ScanResult> results) {Map<String, ScanResult> uniqueMap = new HashMap<>();for (ScanResult result : results) {String ssid = result.SSID;// 跳过空SSIDif (ssid.isEmpty()) continue;// 若SSID未存在,或当前信号更强,则更新if (!uniqueMap.containsKey(ssid) || result.level > uniqueMap.get(ssid).level) {uniqueMap.put(ssid, result);}}return new ArrayList<>(uniqueMap.values());}// 按规则排序:当前连接>已保存>未保存,同组内信号强在前private void sortWifiList(List<ScanResult> results) {if (results == null || results.isEmpty()) {sortedScanResults = new ArrayList<>();return;}// 实时更新已保存网络列表和当前连接SSIDconfiguredNetworks = wifiManager.getConfiguredNetworks();currentConnectedSSID = getCurrentConnectedWifiSSID();Collections.sort(results, new Comparator<ScanResult>() {@Overridepublic int compare(ScanResult r1, ScanResult r2) {// 优先级1:当前连接的WiFi置顶boolean isConn1 = r1.SSID.equals(currentConnectedSSID);boolean isConn2 = r2.SSID.equals(currentConnectedSSID);if (isConn1 && !isConn2) return -1; // r1在前if (!isConn1 && isConn2) return 1; // r2在前// 优先级2:已保存的WiFi排在未保存前面boolean isSaved1 = isWifiSaved(r1.SSID);boolean isSaved2 = isWifiSaved(r2.SSID);if (isSaved1 && !isSaved2) return -1;if (!isSaved1 && isSaved2) return 1;// 优先级3:信号强度降序(level越大信号越强)return Integer.compare(r2.level, r1.level);}});sortedScanResults = results;}// 判断WiFi是否已保存private boolean isWifiSaved(String ssid) {if (configuredNetworks == null || configuredNetworks.isEmpty()) {return false;}// 已保存网络的SSID带双引号String targetSSID = "\"" + ssid + "\"";for (WifiConfiguration config : configuredNetworks) {if (targetSSID.equals(config.SSID)) {return true;}}return false;}// 获取已保存的WiFi配置private WifiConfiguration getSavedWifiConfig(String ssid) {if (configuredNetworks == null || configuredNetworks.isEmpty()) {return null;}String targetSSID = "\"" + ssid + "\"";for (WifiConfiguration config : configuredNetworks) {if (targetSSID.equals(config.SSID)) {return config;}}return null;}// 连接WiFi的核心方法private void connect(String ssid, String password, String capabilities) {// 先检查是否已保存该WiFi(防止重复保存)WifiConfiguration savedConfig = getSavedWifiConfig(ssid);if (savedConfig != null) {connectToSavedWifi(ssid);return;}// 未保存:创建新配置并保存WifiConfiguration newConfig = new WifiConfiguration();newConfig.SSID = "\"" + ssid + "\"";// 根据加密类型设置配置if (capabilities.contains("WEP")) {newConfig.wepKeys[0] = "\"" + password + "\"";newConfig.wepTxKeyIndex = 0;newConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);newConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);} else if (capabilities.contains("WPA")) {newConfig.preSharedKey = "\"" + password + "\"";} else {// 开放网络newConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);}// 添加配置并连接int networkId = wifiManager.addNetwork(newConfig);if (networkId != -1) {// 保存配置wifiManager.saveConfiguration();// 启用该网络并断开其他网络wifiManager.disconnect();boolean isConnected = wifiManager.enableNetwork(networkId, true);wifiManager.reconnect();if (isConnected) {Toast.makeText(this, "正在连接:" + ssid, Toast.LENGTH_SHORT).show();// 更新已保存列表configuredNetworks = wifiManager.getConfiguredNetworks();} else {// 连接失败:删除无效配置wifiManager.removeNetwork(networkId);wifiManager.saveConfiguration();Toast.makeText(this, "连接失败,请检查密码", Toast.LENGTH_SHORT).show();}} else {Toast.makeText(this, "无法添加网络配置", Toast.LENGTH_SHORT).show();}}// 检查权限并启用WiFiprivate void checkPermissionsAndEnableWifi() {if (hasRequiredPermissions()) {wifiManager.setWifiEnabled(true);} else {requestPermissions();}}// 检查权限并扫描WiFiprivate void checkPermissionsAndScan() {if (hasRequiredPermissions()) {startWifiScan();} else {requestPermissions();}}// 检查必要权限private boolean hasRequiredPermissions() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {return ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION)== PackageManager.PERMISSION_GRANTED;}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {return ContextCompat.checkSelfPermission(this, android.Manifest.permission.NEARBY_WIFI_DEVICES)== PackageManager.PERMISSION_GRANTED;}return true;}// 请求必要权限private void requestPermissions() {List<String> permissions = new ArrayList<>();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {permissions.add(android.Manifest.permission.ACCESS_FINE_LOCATION);}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {permissions.add(android.Manifest.permission.NEARBY_WIFI_DEVICES);}ActivityCompat.requestPermissions(this,permissions.toArray(new String[0]),PERMISSION_REQUEST_CODE);}// 处理权限请求结果@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == PERMISSION_REQUEST_CODE) {boolean hasPermission = true;for (int result : grantResults) {if (result != PackageManager.PERMISSION_GRANTED) {hasPermission = false;break;}}if (hasPermission) {if (wifiSwitch.isChecked() && !wifiManager.isWifiEnabled()) {wifiManager.setWifiEnabled(true);}} else {Toast.makeText(this, "需要位置/附近WiFi权限才能使用扫描功能", Toast.LENGTH_SHORT).show();wifiSwitch.setChecked(false);}}}// 开始扫描WiFiprivate void startWifiScan() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isForeground()) {Toast.makeText(this, "应用需要在前台才能扫描WiFi", Toast.LENGTH_SHORT).show();return;}boolean started = wifiManager.startScan();if (!started) {Toast.makeText(this, "扫描失败,请重试", Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "开始扫描...", Toast.LENGTH_SHORT).show();}}// 检查应用是否在前台private boolean isForeground() {ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();if (appProcesses == null) {return false;}final String packageName = getPackageName();for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {if (appProcess.processName.equals(packageName) &&appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {return true;}}return false;}// WiFi列表适配器(确保已连接标识正确显示)private class WifiListAdapter extends BaseAdapter {@Overridepublic int getCount() {return (sortedScanResults != null) ? sortedScanResults.size() : 0;}@Overridepublic Object getItem(int position) {return (sortedScanResults != null) ? sortedScanResults.get(position) : null;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if (convertView == null) {convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.wifi_list_item, parent, false);// 使用ViewHolder模式优化性能holder = new ViewHolder();holder.ssidText = convertView.findViewById(R.id.ssid_text);holder.strengthText = convertView.findViewById(R.id.strength_text);holder.securityText = convertView.findViewById(R.id.security_text);holder.connectedTv = convertView.findViewById(R.id.tv_connected);holder.savedIndicator = convertView.findViewById(R.id.saved_indicator);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}if (sortedScanResults != null && position < sortedScanResults.size()) {ScanResult result = sortedScanResults.get(position);boolean isConnected = result.SSID.equals(currentConnectedSSID);boolean isSaved = isWifiSaved(result.SSID);// 显示SSID和已连接标识holder.ssidText.setText(result.SSID);holder.connectedTv.setVisibility(isConnected ? View.VISIBLE : View.GONE);// 显示已保存标识(可选)holder.savedIndicator.setVisibility(isSaved && !isConnected ? View.VISIBLE : View.GONE);// 信号强度holder.strengthText.setText(String.format("信号强度: %d dBm", result.level));// 加密方式String security = "开放网络";if (result.capabilities.contains("WPA3")) {security = "WPA3";} else if (result.capabilities.contains("WPA2")) {security = "WPA2";} else if (result.capabilities.contains("WPA")) {security = "WPA";} else if (result.capabilities.contains("WEP")) {security = "WEP";}holder.securityText.setText("加密方式: " + security);}return convertView;}// ViewHolder内部类private class ViewHolder {TextView ssidText;TextView strengthText;TextView securityText;TextView connectedTv;TextView savedIndicator;}}
}
一、WiFi 状态控制接口
setWifiEnabled(boolean enabled)
- 作用:开启或关闭 WiFi 功能。
- 参数:
enabled=true
开启,enabled=false
关闭。 - 返回值:
boolean
,true
表示操作成功(系统可能因权限 / 策略限制返回false
)。 - 权限:需
android.permission.CHANGE_WIFI_STATE
权限。 - 场景:用户点击 WiFi 开关时调用,控制硬件开关状态。
getWifiState()
- 作用:获取当前 WiFi 的状态。
- 返回值int,可能值:
WIFI_STATE_DISABLED
(已关闭)、WIFI_STATE_ENABLED
(已开启);WIFI_STATE_DISABLING
(正在关闭)、WIFI_STATE_ENABLING
(正在开启);WIFI_STATE_UNKNOWN
(未知状态)。
- 权限:需
android.permission.ACCESS_WIFI_STATE
权限。 - 场景:监听 WiFi 开关状态变化,更新 UI(如开关按钮状态)。
isWifiEnabled()
- 作用:快速判断 WiFi 是否处于 “已开启” 状态(简化版
getWifiState()
)。 - 返回值:
boolean
,true
表示已开启,false
表示关闭或正在切换。 - 权限:需
android.permission.ACCESS_WIFI_STATE
权限。 - 场景:发起扫描前检查 WiFi 是否开启,避免无效操作。
- 作用:快速判断 WiFi 是否处于 “已开启” 状态(简化版
二、WiFi 扫描相关接口
startScan()
- 作用:触发系统扫描周围可用的 WiFi 网络(2.4G/5G 频段)。
- 返回值:
boolean
,true
表示扫描请求已提交(不代表扫描成功),false
表示失败(如后台限制、硬件忙)。 - 权限
- Android 12+ 需
android.permission.NEARBY_WIFI_DEVICES
; - Android 10-11 需
android.permission.ACCESS_FINE_LOCATION
; - 且应用需在前台(Android M+ 限制)。
- Android 12+ 需
- 场景:用户点击 “刷新 WiFi 列表” 时调用,触发扫描并等待结果。
getScanResults()
- 作用:获取最近一次扫描的结果(列表形式)。
- 返回值:
List
,每个ScanResult
包含 SSID(WiFi 名称)、level(信号强度,dBm)、capabilities(加密方式)等信息。 - 权限:同
startScan()
(需位置 / 附近 WiFi 权限)。 - 注意:结果是缓存的,需配合
SCAN_RESULTS_AVAILABLE_ACTION
广播监听扫描完成后再调用。 - 场景:扫描完成后,解析并展示 WiFi 列表(如信号强度排序、加密方式标识)。
三、WiFi 连接与配置管理接口
addNetwork(WifiConfiguration config)
- 作用:向系统添加一个新的 WiFi 配置(SSID、密码、加密方式等),用于后续连接。
- 参数:
WifiConfiguration
对象,需设置SSID
(带双引号,如"MyWiFi"
)、加密方式(WPA/WEP/ 开放)、密码等。 - 返回值:
int
,新增配置的唯一标识networkId
(-1
表示添加失败)。 - 权限:需
android.permission.CHANGE_WIFI_STATE
权限。 - 场景:首次连接新 WiFi 时,创建配置并添加到系统(为 “保存功能” 基础)。
enableNetwork(int networkId, boolean disableOthers)
- 作用:启用指定
networkId
的 WiFi 配置,尝试连接该网络。 - 参数
networkId
:通过addNetwork()
或getConfiguredNetworks()
获取的配置 ID;disableOthers
:true
表示断开其他网络,优先连接当前配置。
- 返回值:
boolean
,true
表示启用请求成功(不代表连接成功)。 - 权限:需
android.permission.CHANGE_WIFI_STATE
权限。 - 场景:连接已保存的 WiFi 时(直接用
networkId
),或新配置添加后触发连接。
- 作用:启用指定
getConfiguredNetworks()
- 作用:获取系统中所有已保存的 WiFi 配置列表。
- 返回值:
List
,包含已保存网络的SSID
、networkId
、加密方式等信息。 - 权限:需
android.permission.ACCESS_WIFI_STATE
权限。 - 场景:判断某个 WiFi 是否已保存(如 “已保存网络直接连接” 功能)、展示用户的历史连接记录。
四、连接状态信息接口
-
getConnectionInfo()
-
作用:获取当前已连接 WiFi 的详细信息。
-
返回值
WifiInfo
对象,包含:getSSID()
:当前连接的 WiFi 名称(可能带双引号,需处理);getNetworkId()
:当前连接的配置 ID;getRssi()
:当前信号强度(dBm);getIpAddress()
:设备获取的 IP 地址等。
-
权限:需
android.permission.ACCESS_WIFI_STATE
权限。 -
场景:展示 “当前连接的 WiFi 名称及信号强度”、判断是否已连接目标网络。
-
关键广播(配合接口使用)
WIFI_STATE_CHANGED_ACTION
:监听 WiFi 开关状态变化(配合getWifiState()
使用)。SCAN_RESULTS_AVAILABLE_ACTION
:监听扫描结果是否更新(配合getScanResults()
使用)。NETWORK_STATE_CHANGED_ACTION
:监听 WiFi 连接状态变化(如连接成功 / 失败、断开连接,配合getConnectionInfo()
使用)。