鸿蒙NEXT Wi-Fi扫描开发指南:从基础到实战

在移动应用开发中,Wi-Fi扫描功能是实现网络连接、位置服务等功能的基石,掌握鸿蒙NEXT的Wi-Fi开发能力至关重要。

在鸿蒙应用开发中,Wi-Fi管理是常见的需求之一。无论是扫描附近热点、连接指定网络,还是获取网络状态,鸿蒙NEXT都提供了一套完整的API。

本文将带你全面了解鸿蒙NEXT中Wi-Fi扫描的开发方法,从基础概念到实战应用,帮助你快速掌握这一核心技能。

一、Wi-Fi扫描基础概念

在鸿蒙NEXT中,Wi-Fi扫描主要涉及Station模式。STA是支持IEEE802.11协议的设备,通常指具有Wi-Fi client行为的设备,可以连接到接入点。

STA会扫描可连接的接入点,选择一个想要连接的接入点,经过认证、关联等步骤后,与接入点建立连接。

Wi-Fi扫描有两种不同的方式:

  • 主动扫描:由Wi-Fi client在每个信道上发送探测请求帧,接入点收到探测请求后返回探测响应

  • 被动扫描:Wi-Fi Client在每个信道上监听接入点发出的信标帧

二、开发前准备

1. 模块导入

在鸿蒙NEXT中,Wi-Fi相关功能主要通过@ohos.wifiManager@kit.ConnectivityKit模块提供:

javascript

复制代码
import wifi from '@ohos.wifi'; // API version 6及以上
// 或
import { wifiManager } from '@kit.ConnectivityKit';

2. 权限声明

module.json5配置文件中声明必要的Wi-Fi权限:

json

复制代码
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.GET_WIFI_INFO",
        "reason": "需要获取Wi-Fi信息",
        "usedScene": {
          "abilities": [
            "MainAbility"
          ],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.SET_WIFI_INFO",
        "reason": "需要配置Wi-Fi连接",
        "usedScene": {
          "abilities": [
            "MainAbility"
          ],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.LOCATION",
        "reason": "需要访问位置信息以进行Wi-Fi扫描",
        "usedScene": {
          "abilities": [
            "MainAbility"
          ],
          "when": "always"
        }
      }
    ]
  }
}

注意 :某些API如getScanInfos()需要ohos.permission.GET_WIFI_INFO以及ohos.permission.GET_WIFI_PEERS_MACohos.permission.LOCATION权限中的至少一个。

三、Wi-Fi扫描实现

1. 检查Wi-Fi状态

在进行Wi-Fi扫描前,应先检查Wi-Fi是否已激活:

javascript

复制代码
import wifi from '@ohos.wifi';

// 查询WLAN是否已激活
let isActive = wifi.isWifiActive();
console.log(`Wi-Fi is active: ${isActive}`);

// 如果Wi-Fi未激活,需要先启用
if (!isActive) {
  // 注意:需要ohos.permission.MANAGE_WIFI_SETTINGS权限
  wifi.enableWifi();
}

2. 启动Wi-Fi扫描

使用scan()方法启动Wi-Fi扫描:

javascript

复制代码
// 启动WLAN扫描
let scanResult = wifi.scan();
if (scanResult) {
  console.log("Wi-Fi扫描启动成功");
} else {
  console.error("Wi-Fi扫描启动失败");
}

scan()方法需要ohos.permission.SET_WIFI_INFOohos.permission.LOCATION权限,返回true表示扫描操作执行成功,false表示扫描操作执行失败。

3. 获取扫描结果

获取扫描结果有两种异步方式:Promise和callback。

使用Promise方式:

javascript

复制代码
wifi.getScanInfos().then(result => {
  let len = Object.keys(result).length;
  console.log("Wi-Fi扫描结果数量: " + len);
  for (let i = 0; i < len; ++i) {
    console.info("SSID: " + result[i].ssid);
    console.info("BSSID: " + result[i].bssid);
    console.info("信号强度: " + result[i].rssi);
    console.info("安全类型: " + result[i].securityType);
    console.info("频段: " + result[i].band);
    console.info("频率: " + result[i].frequency);
  }
}).catch(error => {
  console.error("获取扫描结果失败: " + error);
});

使用Callback方式:

javascript

复制代码
wifi.getScanInfos((err, result) => {
  if (err) {
    console.error("获取扫描结果错误: " + JSON.stringify(err));
    return;
  }
  
  var len = Object.keys(result).length;
  console.log("Wi-Fi接收到扫描信息: " + len);
  for (var i = 0; i < len; ++i) {
    console.info("SSID: " + result[i].ssid);
    console.info("BSSID: " + result[i].bssid);
    console.info("能力: " + result[i].capabilities);
    console.info("安全类型: " + result[i].securityType);
    console.info("信号强度: " + result[i].rssi);
    console.info("频段: " + result[i].band);
    console.info("频率: " + result[i].frequency);
    console.info("信道宽度: " + result[i].channelWidth);
    console.info("时间戳: " + result[i].timestamp);
  }
});

4. 扫描结果数据结构

获取到的扫描结果是WifiScanInfo对象的数组,包含以下属性:

字段名 类型 说明
ssid string 热点的SSID,编码格式为UTF-8
bssid string 热点的BSSID
capabilities string 热点能力
securityType WifiSecurityType WLAN加密类型
rssi number 热点的信号强度(dBm)
band number WLAN接入点的频段
frequency number WLAN接入点的频率
channelWidth number WLAN接入点的带宽
timestamp number 时间戳

四、高级功能与实战应用

1. 获取信号等级

可以根据信号强度(rssi)和频段(band)获取用户更易理解的信号等级:

javascript

复制代码
import { wifiManager } from '@kit.ConnectivityKit';

// 对扫描结果按信号强度排序
let scanList = wifiManager.getScanInfoList().sort((a, b) => {
  return wifiManager.getSignalLevel(b.rssi, b.band) - wifiManager.getSignalLevel(a.rssi, a.band);
});

// 显示带信号等级的Wi-Fi列表
scanList.forEach((wifiInfo, index) => {
  let signalLevel = wifiManager.getSignalLevel(wifiInfo.rssi, wifiInfo.band);
  console.log(`${index + 1}. SSID: ${wifiInfo.ssid}, 信号等级: ${signalLevel}/5`);
});

2. 连接Wi-Fi热点

扫描到Wi-Fi热点后,通常需要连接功能:

javascript

复制代码
async function connectToWifi(wifiInfo, password) {
  try {
    // 如果已连接,先断开当前连接
    if (wifiManager.isConnected()) {
      // 这里添加断开连接的逻辑
    }
    
    // 创建Wi-Fi设备配置
    let config = {
      ssid: wifiInfo.ssid,
      preSharedKey: password,
      securityType: wifiInfo.securityType
    };
    
    // 添加候选网络配置
    let netId = await wifiManager.addCandidateConfig(config);
    
    // 连接到候选网络
    wifiManager.connectToCandidateConfig(netId);
    
    console.log("开始连接Wi-Fi");
  } catch (error) {
    console.error("连接Wi-Fi失败: " + JSON.stringify(error));
  }
}

注意connectToCandidateConfigaddCandidateConfig方法需要ohos.permission.SET_WIFI_INFO权限。

3. 获取当前连接信息

获取当前连接的Wi-Fi信息:

javascript

复制代码
// 获取当前连接的Wi-Fi信息
wifiManager.getLinkedInfo().then(data => {
  console.info("获取Wi-Fi连接信息: " + JSON.stringify(data));
}).catch(error => {
  console.info("获取连接信息错误");
});

4. 监听网络状态变化

实时监控网络状态变化对于提供良好的用户体验至关重要:

javascript

复制代码
import { connection } from '@kit.NetworkKit';

// 创建网络连接监听器
const netConnection = connection.createNetConnection();

// 监听网络可用事件
netConnection.on('netAvailable', (netHandle) => {
  console.log(`🎉 网络切换成功!新网络ID:${netHandle.netId}`);
});

// 监听网络丢失事件
netConnection.on('netLost', (netHandle) => {
  console.log('网络连接丢失');
});

// 注册监听
netConnection.register();

// 在适当的时候取消监听
// netConnection.unregister();

五、完整示例

以下是一个完整的Wi-Fi扫描与连接示例:

javascript

复制代码
import { wifiManager } from '@kit.ConnectivityKit';
import { connection } from '@kit.NetworkKit';

@Entry
@Component
struct WifiScanner {
  @State wifiList: Array<wifiManager.WifiScanInfo> = []
  @State isScanning: boolean = false

  // 扫描Wi-Fi热点
  scanWifi() {
    this.isScanning = true
    
    // 注意:实际开发中需要处理权限请求
    try {
      let result = wifiManager.scan();
      if (result) {
        // 模拟扫描延迟,实际开发中使用事件监听
        setTimeout(() => {
          this.wifiList = wifiManager.getScanInfoList().sort((a, b) => {
            return wifiManager.getSignalLevel(b.rssi, b.band) - 
                   wifiManager.getSignalLevel(a.rssi, a.band);
          });
          this.isScanning = false;
        }, 3000);
      }
    } catch (error) {
      console.error("扫描失败: " + JSON.stringify(error));
      this.isScanning = false;
    }
  }

  // 连接Wi-Fi热点
  async connectToWifi(wifiInfo: wifiManager.WifiScanInfo, password: string) {
    try {
      if (wifiManager.isConnected()) {
        // 提示用户先断开当前连接
        console.log("请先断开当前Wi-Fi连接");
        return;
      }
      
      let config = {
        ssid: wifiInfo.ssid,
        preSharedKey: password,
        securityType: wifiInfo.securityType
      };
      
      let netId = await wifiManager.addCandidateConfig(config);
      wifiManager.connectToCandidateConfig(netId);
      
      console.log("开始连接至: " + wifiInfo.ssid);
    } catch (error) {
      console.error("连接失败: " + JSON.stringify(error));
    }
  }

  build() {
    Column() {
      Text('Wi-Fi热点扫描')
        .fontSize(20)
        .margin(10)
      
      Button(this.isScanning ? '扫描中...' : '扫描热点')
        .onClick(() => {
          this.scanWifi();
        })
        .width('80%')
        .margin(10)
        .enabled(!this.isScanning)
      
      List() {
        ForEach(this.wifiList, (item: wifiManager.WifiScanInfo) => {
          ListItem() {
            WifiItem({
              wifiInfo: item,
              connectCallback: this.connectToWifi.bind(this)
            })
          }
        })
      }
      .layoutWeight(1)
      .width('100%')
    }
    .height('100%')
  }
}

@Component
struct WifiItem {
  @Param wifiInfo: wifiManager.WifiScanInfo
  @Param connectCallback: (wifiInfo: wifiManager.WifiScanInfo, password: string) => void
  @State password: string = ''
  @State showPasswordInput: boolean = false

  build() {
    Column() {
      Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
        Text(this.wifiInfo.ssid || '隐藏网络')
          .fontSize(16)
          .flexGrow(1)
        
        Text(`信号: ${wifiManager.getSignalLevel(this.wifiInfo.rssi, this.wifiInfo.band)}/5`)
          .fontSize(12)
          .margin({ right: 10 })
        
        Button('连接')
          .onClick(() => {
            if (this.wifiInfo.securityType !== wifiManager.WifiSecurityType.WIFI_SEC_TYPE_OPEN) {
              this.showPasswordInput = true;
            } else {
              this.connectCallback(this.wifiInfo, '');
            }
          })
          .fontSize(12)
      }
      .width('100%')
      
      if (this.showPasswordInput) {
        TextInput({ placeholder: '输入Wi-Fi密码', text: this.password })
          .onChange((value: string) => {
            this.password = value;
          })
          .width('100%')
          .margin({ top: 5 })
        
        Button('确认连接')
          .onClick(() => {
            this.connectCallback(this.wifiInfo, this.password);
            this.showPasswordInput = false;
          })
          .width('100%')
          .margin({ top: 5 })
      }
    }
    .padding(10)
    .border({ width: 1, color: '#CCCCCC' })
    .margin({ top: 5, bottom: 5 })
  }
}

六、常见问题与解决方案

1. 权限问题

问题:调用Wi-Fi相关API时返回权限错误。

解决

  • 确保在module.json5中声明了所需权限

  • 在运行时动态请求必要权限

  • 检查权限名称是否正确

2. 扫描结果为空

问题:获取到的扫描结果列表为空。

解决

  • 检查是否已授予位置权限

  • 确认Wi-Fi已启用

  • 添加适当的延迟后再获取扫描结果

3. 连接失败

问题:无法连接到指定的Wi-Fi热点。

解决

  • 检查SSID和密码是否正确

  • 确认安全类型匹配

  • 确保设备不在飞行模式下

七、总结

通过本文,我们全面介绍了鸿蒙NEXT中Wi-Fi扫描的开发方法,包括:

  1. 基础概念:了解Wi-Fi扫描的两种方式

  2. 权限配置:正确声明和请求Wi-Fi相关权限

  3. 扫描实现:使用API进行Wi-Fi扫描和结果获取

  4. 高级功能:信号等级评估、热点连接和网络状态监听

  5. 实战示例:完整的Wi-Fi扫描和连接组件

Wi-Fi扫描是鸿蒙应用开发中的常见需求,掌握这些技能将为你的应用增添强大的网络功能。希望本文能帮助你在鸿蒙NEXT开发中顺利实现Wi-Fi相关功能。

相关推荐
安卓开发者3 小时前
在鸿蒙NEXT中发起HTTP网络请求:从入门到精通
网络·http·harmonyos
米羊1215 小时前
【鸿蒙心迹】工匠雕琢,攻克难题(下)
华为·harmonyos
SmartBrain21 小时前
华为业务流程架构:主干清晰、末端灵活
华为·创业创新
chenbin___21 小时前
鸿蒙键盘遮挡react native内容尝试
react native·harmonyos
爱笑的眼睛1121 小时前
深入解析HarmonyOS ArkTS:从语法特性到实战应用
华为·harmonyos
安卓开发者1 天前
鸿蒙NEXT SSAP连接与数据传输实战:轻松实现跨设备通信
华为·harmonyos
我是华为OD~HR~栗栗呀1 天前
Java面经(22届考研-华oD)
java·后端·python·华为od·华为
yenggd1 天前
QoS之流量整形配置方法
网络·数据库·华为
安卓开发者1 天前
在鸿蒙NEXT中使用WebSocket实现实时网络通信
websocket·华为·harmonyos