wlan-search-connect-disconnect.md 16.7 KB
Newer Older
W
wangqingkaihong 已提交
1 2 3 4 5 6 7 8 9 10
# 如何实现WLAN网络扫描、连接、断开

## 场景说明

对可用的WLAN列表进行扫描,连接、断开WLAN网络是设备常见的功能,本例将为大家介绍如何实现上述功能。

## 效果呈现

本例效果如下:

Z
zhangchao 已提交
11 12
|                           扫描WLAN                           |                           连接WLAN                           |                           断开WLAN                           |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
W
wangqingkaihong 已提交
13
| <img src="figures/wlanscan.png" alt="contactlist" style="zoom: 50%;" /> | <img src="figures/wlanconnect.png" alt="contactlist" style="zoom: 50%;" /> | <img src="figures/wlandisconnect.png" alt="contactlist" style="zoom: 50%;" /> |
W
wangqingkaihong 已提交
14 15 16

## 运行环境

Z
zhangchao 已提交
17 18
本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发。

W
wangqingkaihong 已提交
19 20 21 22 23
- IDE:DevEco Studio 4.0.0.201 Beta1
- SDK:Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1)

## 实现思路

Z
zhangchao 已提交
24
本例主要通过@ohos.wifiManager 相关API实现WLAN激活和关闭、扫描和连接WLAN等功能。
W
wangqingkaihong 已提交
25

Z
zhangchao 已提交
26 27 28
- WLAN功能的激活和关闭:点击首页的切换按钮,如果是打开,使用wifi.enableWifi()开启WLAN功能;如果是关闭,则使用wifi.disconnect()断开WLAN功能, 然后使用wifi.disableWifi()关闭WLAN。
- WLAN的连接:点击WLAN列表中加密的WLAN,并在弹窗中输入密码后,会在AvailableWifi.ets中通过WifiModule.connectNetwork()调用wifi.connectToDevice()连接WLAN。
- WLAN的扫描:进入Index.ets 后就会间歇性的调用wifi.scan()开启扫描,然后通过WifiModel模块中的getScanInfos()调用wifi.getScanResults()来获取扫描的结果.
W
wangqingkaihong 已提交
29 30 31

## 开发步骤

Z
zhangchao 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
1. 申请所需权限

   在model.json5中添加以下配置:

   ```json
   "requestPermissions": [
     {
       "name": "ohos.permission.GET_WIFI_INFO"//允许应用获取WLAN信息
     },
     {
       "name": "ohos.permission.GET_WIFI_INFO_INTERNAL"//允许应用获取WLAN信息
     },
     {
       "name": "ohos.permission.SET_WIFI_INFO"//允许应用配置WLAN设备
     },
     {
       "name": "ohos.permission.GET_WIFI_PEERS_MAC"//允许应用获取对端WLAN或者蓝牙设备的MAC地址
     },
     {
       "name": "ohos.permission.GET_WIFI_LOCAL_MAC"//允许应用获取本机WLAN或者蓝牙设备的MAC地址
     },
     {
       "name": "ohos.permission.GET_WIFI_CONFIG"//允许应用获取WLAN配置信息
     },
     {
       "name": "ohos.permission.SET_WIFI_CONFIG"//允许应用配置WLAN信息
     },
     {
       "name": "ohos.permission.MANAGE_WIFI_CONNECTION"//允许应用管理WLAN连接
     },
     {
       "name": "ohos.permission.MANAGE_WIFI_HOTSPOT"//允许应用开启或者关闭WLAN热点
     },
     {
       "name": "ohos.permission.LOCATION"//允许应用获取设备位置信息
     },
     {
       "name": "ohos.permission.APPROXIMATELY_LOCATION"//允许应用获取设备模糊位置信息
     }
   ]
   ```

2. 准备工作

   定义boolean变量isSwitchOn作为WLAN开关是否打开的标志。

   ```typescript
   import wifi from '@ohos.wifiManager'
   aboutToAppear() {
       // 如果WLAN是开的,就记录下状态,然后扫描WLAN,并获取连接信息
       if (wifi.isWifiActive()) {//查询WLAN是否已使能
         Logger.log(TAG, 'wifi is active')
         this.isSwitchOn = true//已使能说明WLAN开关已被打开,那么让isSwitchOn为true
         wifi.scan()//@ohos.wifiManager的接口,对WLAN进行扫描
         this.scan()//自定义的scan函数,确保扫描的持续性
         this.getLinkedInfo()
       }
       // 启动监听
       this.addListener()//监听连接状态的变化
     }
   ```

3. 持续地扫描可用WLAN

   scan方法不断地自我调用以实现对WLAN的不停扫描。

   ```typescript
   async getLinkedInfo() {
       try {
         //调用getLinkedInfo接口得到是否已连接WLAN的信息 
         let wifiLinkedInfo = await wifi.getLinkedInfo()
         if (wifiLinkedInfo === null || wifiLinkedInfo.bssid === '') {
             //如果结果为空,则说明未连接
           this.isLinked = false
           this.linkedInfo = null
           return
         }
         this.isLinked = true
         this.linkedInfo = wifiLinkedInfo
       } catch (err) {
         Logger.info(`getLinkedInfo failed err is ${JSON.stringify(err)}`)
       }
     }
   async scan() {
       // 获取有关WLAN连接的信息,存入linkedInfo
       await this.getLinkedInfo()
       // 不停地扫描WLAN
       let result: Array<WifiType> = await this.wifiModel.getScanInfos()//调用model/WifiModel.ets中的getScanInfos得到扫描结果
       if (this.isSwitchOn) {
           //如果WLAN功能已打开,则要进行不停地扫描
         AppStorage.SetOrCreate('wifiList', result)//将扫描结果存至全局
           //每3000毫秒调用一次scan(),实现不停扫描可用WLAN
         setTimeout(async () => {
           await this.scan()
         }, 3000)
       }
     }
   addListener() {
       // WLAN连接状态改变事件发生时,修改连接信息
       wifi.on('wifiConnectionChange', async state => {
         Logger.log(TAG, `wifiConnectionChange: ${state}`)
         await this.getLinkedInfo()//WLAN连接状态改变,重新获取WLAN连接信息
       })
       // WLAN状态改变时,先清空WLAN列表,然后判断是否是开启状态,如果是就扫描
       wifi.on('wifiStateChange', state => {
         Logger.log(TAG, `wifiStateLisener state: ${state}`)
         AppStorage.SetOrCreate('wifiList', [])
         if (state === 1) { // 1: wifi is enable, 0:wifi is disable
           this.scan()
         }
       })
     }
   ...
   //model/WifiModel.ets
   async getScanInfos(): Promise<Array<WifiType>> {
       Logger.log(TAG, 'scanWifi begin')
       let wifiList: Array<WifiType> = []
       let result: Array<wifi.WifiScanInfo> = []
       try {
         result = await wifi.getScanResults()//因为在abouttoappear()中执行了wifi.scan(),所以这里直接调用getScanResults接口得到扫描结果
       } catch (err) {
         Logger.log(TAG, `scan info err: ${JSON.stringify(err)}`)
         return wifiList
       }
       Logger.log(TAG, `scan info call back: ${result.length}`)
       for (var i = 0; i < result.length; ++i) {
         wifiList.push({
           ssid: result[i].ssid,
           bssid: result[i].bssid,
           securityType: result[i].securityType,
           rssi: result[i].rssi,
           band: result[i].band,
           frequency: result[i].frequency,
           timestamp: result[i].timestamp
         })
       }
       return wifiList
     }
   ```

4. 构建UI框架、通过关闭按钮实现断开WLAN连接。

   TitleBar组件呈现了标题框。WLAN开关按钮由一个Toggle组件呈现,其绑定的onChange事件在按钮被打开时开始扫描可用WLAN,在被关闭时断开当前连接WLAN并且关闭WLAN功能。当前已连接的WLAN由两个Text组件分别显示其ssid与连接状态。

   ```typescript
   build() {
       Column() {
         TitleBar()//引入自component/TitleBar.ets,负责标题框的具体呈现
         Row() {
           Text($r('app.string.wlan'))
             .fontSize(22)
             .fontWeight(FontWeight.Bold)
             .height(40)
           Column() {
             Toggle({ type: ToggleType.Switch, isOn: this.isSwitchOn })//切换按钮
               .id('switch')
               .onChange((isOn: boolean) => {
                 Logger.log(`LSQ: wifi swtich is: ${isOn}`)
                 AppStorage.SetOrCreate('wifiList', [])
                 try {
                   // 如果按钮被打开了,记录状态,打开网络,开始扫描可用WLAN
                   if (isOn) {
                     this.isSwitchOn = true
                     wifi.enableWifi()//使WLAN功能可用
                     return
                   } else {
                     // 如果按钮被关闭了记录状态,断开网络禁用网络
                     this.isSwitchOn = false
                     this.isLinked = false
                     wifi.disconnect()//断开当前WLAN连接
                     wifi.disableWifi()//使WLAN功能不可用
                   }
                 } catch (error) {
                   Logger.error(TAG, `failed,code:${JSON.stringify(error.code)},message:${JSON.stringify(error.message)}`)
                 }
               })
           }
         }
         .width('100%')
         .padding({ left: 16, right: 16 })
         if (this.isLinked && this.isSwitchOn) {
             //如果当前按钮处于打开状态,且WLAN是已连接状态
           Column() {
             Text($r('app.string.connected'))
               .fontSize(22)
               .width('100%')
             Row() {
               Text(this.linkedInfo.ssid)//展示当前已连接的WLAN
                 .fontSize(20)
                 .fontColor(Color.Black)
                 .layoutWeight(1)
               Text($r('app.string.connected'))
                 .fontSize(18)
                 .fontColor(Color.Black)
             }
             .width('100%')
             .padding(10)
             .margin({ left: 16, right: 16 })
             .border({ radius: 15, color: Color.Gray, width: 1 })
             .backgroundColor(Color.White)
           }
           .width('100%')
           .padding({ left: 16, right: 16 })
         }
         if (this.isSwitchOn) {
             //如果按钮处于打开状态,展示所有可用WLAN
           AvailableWifi({ linkedInfo: this.linkedInfo })//AvailableWifi引入自component/AvailableWifi.ets
         }
       }
       .width('100%')
       .height('100%')
       .backgroundColor($r('app.color.index_bg'))
     }
   ```

5. 展示可用WLAN列表及WLAN的连接。

   使用List组件呈现可用WLAN列表,ListItem由一个呈现WLANssid的Text组件,一个表示其是否被加密的Text组件和一个展示其加密与否图像的Image组件组成。

   ```typescript
   //component/AvailableWifi.ets
   build() {
       List() {
         ListItem() {
           Row() {
             Text($r('app.string.available_wlan'))
               .fontSize(22)
               .layoutWeight(1)
           }
           .id('validWlan')
           .width('100%')
         }
   		//通过数据懒加载的方式从数据源中每次迭代一个可用WLAN进行展示,可用列表被放置在滚动容器中,被划出可视区域外的资源会被回收
         LazyForEach(this.wifiDataResource, (item, index) => {
           ListItem() {
             WifiView({ wifi: item })//WifiView引入自Component/WifiView.ets,负责可用WLAN信息展示的具体实现
           }
           .id(`Wifi${index}`)
           //对可用WLAN点击时触发事件
           .onClick(() => {
             Logger.info(TAG, 'wifi click')
             this.scanInfo = item
             if (this.linkedInfo !== null && item.ssid === this.linkedInfo.ssid) {
               prompt.showToast({ message: 'this wifi is connected' })//如果当前已连接WLAN并且点击的就是这个WLAN,创建并显示文本提示框
               return
             }
             if (item.securityType === 0 || item.securityType === 1) {
               //如果点击的这个WLAN的加密类型是无效加密类型或者开放加密类型,调用model/Wifimodel.ets中的connectNetwork方法连接此WLAN
               this.wifiModel.connectNetwork(item, '')
               return
             }
             //如果点击的这个WLAN的加密类型是其他加密类型,则需要输入密码,调用component/PswDialog.ets中的方法进行弹窗的生成
             this.pswDialogController.open()
           })
         }, item => JSON.stringify(item))
       }
       .width('100%')
       .height('100%')
       .padding({ left: 16, right: 16 })
       .layoutWeight(1)
       .divider({ strokeWidth: 1, color: Color.Gray, startMargin: 10, endMargin: 10 })
       .margin({ top: 10 })
     }
   //pswDialogController回调的action函数,将传回的WLAN信息与密码传入model/Wifimodel.ets中的connectNetwork方法中
   onAccept(scanInfo, psw) {
       Logger.info(TAG, 'connect wifi')
       self.wifiModel.connectNetwork(scanInfo, psw)
     }
   ...
   //Component/WifiView.ets
   aboutToAppear() {
       Logger.debug(TAG, `aboutToAppear ${JSON.stringify(this.wifi)}`)
       if (this.wifi) {
         if (this.wifi.securityType) {
           if (this.wifi.securityType === 0 || this.wifi.securityType === 1) {
               //WLAN的加密类型是无效加密类型或者开放加密类型
             this.securityString = $r('app.string.open')
             this.isLock = false
           }
         }
       }
     }
   build() {
       Row() {
         Column() {
           if (this.wifi) {
             if (this.wifi.ssid) {
               Text(this.wifi.ssid)
                 .fontSize(20)
                 .width('100%')
             }
           }
             //如果WLAN的加密类型是无效加密类型或者开放加密类型,显示“开放”,反之显示“加密”
           Text(this.securityString)
             .fontSize(18)
             .fontColor(Color.Gray)
             .width('100%')
         }
         .layoutWeight(1)
   
         Stack({ alignContent: Alignment.BottomEnd }) {
             //如果WLAN的加密类型是无效加密类型或者开放加密类型,显示不带锁的图标,反之显示带锁的图标
           Image($r('app.media.wifi'))
             .height(30)
             .width(30)
             .objectFit(ImageFit.Contain)
           if (this.isLock) {
             Image($r('app.media.lock'))
               .objectFit(ImageFit.Contain)
               .width(15)
               .height(15)
           }
         }
         .width(40)
         .height(40)
         .margin({ right: 10 })
       }
       .backgroundColor(Color.White)
       .width('100%')
       .padding(10)
     }
   }
   ...
   //model/Wifimodel.ets
   connectNetwork(scanInfo: wifi.WifiScanInfo, psw) {
       prompt.showToast({ message: 'connecting', duration: 5000 })
       Logger.debug(TAG, `connectNetwork bssid=${scanInfo.bssid}`)
       // 这里因为api问题,需要声明为any,已提单
       let deviceConfig: any = {
         ssid: scanInfo.ssid,
         bssid: scanInfo.bssid,
         preSharedKey: psw,
         isHiddenSsid: false,
         securityType: scanInfo.securityType
       }
       try {
         wifi.connectToDevice(deviceConfig)//连接到指定网络
         Logger.debug(TAG, `connectToDevice success`)
       } catch (err) {
         Logger.debug(TAG, `connectToDevice fail err is ${JSON.stringify(err)}`)
       }
       try {
         wifi.addDeviceConfig(deviceConfig)//添加网络配置
       } catch (err) {
         Logger.debug(TAG, `addDeviceConfig fail err is ${JSON.stringify(err)}`)
       }
     }
   ...
   //component/PswDialog.ets
    build() {
       Column() {
         Text(this.scanInfo.ssid)
           .fontSize(20)
           .width('95%')
   
         TextInput({ placeholder: 'input password' })//密码的输入框
           .id('input')
           .type(InputType.Password)
           .placeholderColor(Color.Gray)
           .fontSize(19)
           .margin({ top: 15 })
           .width('95%')
           .height(36)
           //每当输入框中的内容变化,它就赋值给psw
           .onChange((value: string) => {
             this.psw = value
           })
   
         Row() {
            //确认按钮
           Button() {
             Text($r('app.string.sure'))
               .fontColor(Color.Blue)
               .fontSize(17)
           }
           .id('sure')
           .layoutWeight(7)
           .backgroundColor(Color.White)
           .margin(5)
           .onClick(() => {
             this.controller.close()
             this.action(this.scanInfo, this.psw)
           })
   
           Text()
             .width(1)
             .height(35)
             .backgroundColor($r('app.color.text_color'))
   		//取消按钮
           Button() {
             Text($r('app.string.cancel'))
               .fontColor(Color.Red)
               .fontSize(17)
           }
           .layoutWeight(7)
           .backgroundColor(Color.White)
           .margin(5)
           .onClick(() => {
             this.controller.close()
           })
         }
         .width('100%')
         .margin({ top: '3%' })
       }
       .padding(15)
     }
   ```
W
wangqingkaihong 已提交
439 440 441 442 443 444 445

## 全部代码

本例完整代码sample示例链接:[WLAN](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/Connectivity/Wlan)

## 参考

Z
zhangchao 已提交
446 447
- [权限列表](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/security/permission-list.md#ohospermissiondistributed_datasync)
- [@ohos.wifiManager](../application-dev/reference/apis/js-apis-wifiManager.md)
W
wangqingkaihong 已提交
448