当前位置: 首页 > news >正文

Wifi开发上层学习1:实现一个wifi搜索以及打开的app

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 状态控制接口

  1. setWifiEnabled(boolean enabled)
    • 作用:开启或关闭 WiFi 功能。
    • 参数enabled=true 开启,enabled=false 关闭。
    • 返回值booleantrue 表示操作成功(系统可能因权限 / 策略限制返回 false)。
    • 权限:需 android.permission.CHANGE_WIFI_STATE 权限。
    • 场景:用户点击 WiFi 开关时调用,控制硬件开关状态。
  2. 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(如开关按钮状态)。
  3. isWifiEnabled()
    • 作用:快速判断 WiFi 是否处于 “已开启” 状态(简化版 getWifiState())。
    • 返回值booleantrue 表示已开启,false 表示关闭或正在切换。
    • 权限:需 android.permission.ACCESS_WIFI_STATE 权限。
    • 场景:发起扫描前检查 WiFi 是否开启,避免无效操作。

二、WiFi 扫描相关接口

  1. startScan()
    • 作用:触发系统扫描周围可用的 WiFi 网络(2.4G/5G 频段)。
    • 返回值booleantrue 表示扫描请求已提交(不代表扫描成功),false 表示失败(如后台限制、硬件忙)。
    • 权限
      • Android 12+ 需 android.permission.NEARBY_WIFI_DEVICES
      • Android 10-11 需 android.permission.ACCESS_FINE_LOCATION
      • 且应用需在前台(Android M+ 限制)。
    • 场景:用户点击 “刷新 WiFi 列表” 时调用,触发扫描并等待结果。
  2. getScanResults()
    • 作用:获取最近一次扫描的结果(列表形式)。
    • 返回值List,每个 ScanResult 包含 SSID(WiFi 名称)、level(信号强度,dBm)、capabilities(加密方式)等信息。
    • 权限:同 startScan()(需位置 / 附近 WiFi 权限)。
    • 注意:结果是缓存的,需配合 SCAN_RESULTS_AVAILABLE_ACTION 广播监听扫描完成后再调用。
    • 场景:扫描完成后,解析并展示 WiFi 列表(如信号强度排序、加密方式标识)。

三、WiFi 连接与配置管理接口

  1. addNetwork(WifiConfiguration config)
    • 作用:向系统添加一个新的 WiFi 配置(SSID、密码、加密方式等),用于后续连接。
    • 参数WifiConfiguration 对象,需设置 SSID(带双引号,如 "MyWiFi")、加密方式(WPA/WEP/ 开放)、密码等。
    • 返回值int,新增配置的唯一标识 networkId-1 表示添加失败)。
    • 权限:需 android.permission.CHANGE_WIFI_STATE 权限。
    • 场景:首次连接新 WiFi 时,创建配置并添加到系统(为 “保存功能” 基础)。
  2. enableNetwork(int networkId, boolean disableOthers)
    • 作用:启用指定 networkId 的 WiFi 配置,尝试连接该网络。
    • 参数
      • networkId:通过 addNetwork()getConfiguredNetworks() 获取的配置 ID;
      • disableOtherstrue 表示断开其他网络,优先连接当前配置。
    • 返回值booleantrue 表示启用请求成功(不代表连接成功)。
    • 权限:需 android.permission.CHANGE_WIFI_STATE 权限。
    • 场景:连接已保存的 WiFi 时(直接用 networkId),或新配置添加后触发连接。
  3. getConfiguredNetworks()
    • 作用:获取系统中所有已保存的 WiFi 配置列表。
    • 返回值List,包含已保存网络的 SSIDnetworkId、加密方式等信息。
    • 权限:需 android.permission.ACCESS_WIFI_STATE 权限。
    • 场景:判断某个 WiFi 是否已保存(如 “已保存网络直接连接” 功能)、展示用户的历史连接记录。

四、连接状态信息接口

  1. 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() 使用)。

文章转载自:

http://Fdyvs1F4.ymbqr.cn
http://74boZQEX.ymbqr.cn
http://3M1plOyG.ymbqr.cn
http://ykcmeOTz.ymbqr.cn
http://7Wio4gHt.ymbqr.cn
http://25noKmPo.ymbqr.cn
http://DU4QFbXR.ymbqr.cn
http://pz1sAB9U.ymbqr.cn
http://2wlaReia.ymbqr.cn
http://7Ef4MtmC.ymbqr.cn
http://vfQDCn4L.ymbqr.cn
http://Ef5KaEyI.ymbqr.cn
http://TopkzvNT.ymbqr.cn
http://WJILk1Gq.ymbqr.cn
http://rJUM4nKT.ymbqr.cn
http://XUVKNibc.ymbqr.cn
http://bqDYn91s.ymbqr.cn
http://hsFO1qtJ.ymbqr.cn
http://0Dpv9MiX.ymbqr.cn
http://tRJIogFv.ymbqr.cn
http://WVHyUcoV.ymbqr.cn
http://RGLbST3T.ymbqr.cn
http://mpBTVwNR.ymbqr.cn
http://ZbcOthNC.ymbqr.cn
http://pOm0TL3e.ymbqr.cn
http://oNpFVxxE.ymbqr.cn
http://AmQhMZSN.ymbqr.cn
http://Qswa4tA1.ymbqr.cn
http://RwdsqA5O.ymbqr.cn
http://RWQOAMIQ.ymbqr.cn
http://www.dtcms.com/a/362568.html

相关文章:

  • 零依赖每月工作计划备忘录:高效管理你的每一天
  • Qt 创建的C++ 桌面程序 学习笔记1
  • Elasticsearch创建索引分片和副本大小建议
  • iOS XML 处理利器:CNXMLParser 与 CNXMLDocument 深度解析
  • iOS15如何绕过MDM锁?详细图文教程教你搞定
  • 数据结构:基数排序 (Radix Sort)
  • uni-app iOS 性能监控与调试全流程:多工具协作的实战案例
  • Qt中QSettings的键值使用QDataStream进行存储
  • 【Vue2 ✨】Vue2 入门之旅(七):事件处理
  • 从spring MVC角度理解HTTP协议及Request-Response模式
  • 自学嵌入式第三十二天:网络编程-UDP
  • 基于单片机醉酒驾驶检测系统/酒精检测/防疲劳驾驶设计
  • Angular事件处理全攻略:从基础到进阶的完整指南
  • GEO 应用实践研讨会:探索行业新路径,激发企业新活力
  • IoT Power软件 -- 每次开启强制升级解决方法
  • DVWA靶场通关笔记-DOM型XSS(Impossible级别)
  • CentOS7.6
  • 基于Force-closure评估的抓取计算流程
  • gitlab中回退代码,CI / CD 联系运维同事处理
  • RAGFlow——知识库检索系统开发实战指南(包含聊天和Agent模式)
  • 微信小程序备忘
  • ResponseBodyEmitter介绍
  • HarmonyOS 鸿蒙系统自带的 SymbolGlyph 图标组件详解
  • 【学Python自动化】 8.1 Python 与 Rust 错误处理对比学习笔记
  • 拔河(蓝桥杯)(前缀和)
  • Docker CI/CD 自动化部署配置指南
  • 【Datawhale之Happy-LLM】3种常见的decoder-only模型——Github最火大模型原理与实践教程task07
  • C#---共享项目
  • 【C++变量和数据类型:从基础到高级】
  • AI 在教育领域的落地困境:个性化教学与数据隐私的平衡之道