移动开发之Wifi列表获取功能

news2024/9/24 1:19:07

一、场景

业务需要通过App给设备配置无线网络连接,所以需要App获取附近的WiFi列表,并进行网络连接验证。

二、安卓端实现

1、阅读谷歌官网文档,关于Wifi 接口使用

https://developer.android.com/guide/topics/connectivity/wifi-scan?hl=zh-cn

文档的使用流程说的相当明了清晰,注册--扫描--获取。

但是其提到了关于Android 10 以上版本的特别说明, 而且看到代码中:

标明接口过期,但是实际调试使用,发现在10以上版本中也是能正常接收到广播获取扫描结果的。只要申请号对应的权限:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

权限当然也需要动态申请:

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},WIFI_REQUEST_FOR_PERMISSION);

Android13 权限额外需求:

https://developer.android.com/guide/topics/connectivity/wifi-permissions

NEARBY_WIFI_DEVICES

这个在实际调试过程中发现,加了和没加都能够获取到wifi列表数据。

另外需要注意的是,定位权限是一回事,手机系统有没有打开定义又是另外一回事,所以在使用此功能前要先判断定位开关是否打开:

// 通过GPS卫星定位,定位级别可以精确到街(通过24颗卫星定位,在室外和空旷的地方定位准确、速度快)
boolean gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
// 通过WLAN或移动网络(3G/2G)确定的位置(也称作AGPS,辅助GPS定位。主要用于在室内或遮盖物(建筑群或茂密的深林等)密集的地方定位)
boolean network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (gps || network) {
    return true;
}

再者就是WLAN 的开关有没有打开,安卓10以下的可以直接通过代码设置,10以上的需要跳转到设置界面,引导用户打开:

int wifiState = wifiManager.getWifiState();
if (WifiManager.WIFI_STATE_ENABLED != wifiState){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        ToastUtil.makeText(mContext,"请打开WiFi开关");
        startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
        //startActivity(new Intent(Settings.Panel.ACTION_WIFI));
        finish();
    }else {
        wifiManager.setWifiEnabled(true);
    }
}

2、网络连接验证调试

 为了确认用户输入的密码是正确的,所以想对网络连接进行验证测试,此时就发现安卓10 上下版本的接口差异了:

API29 以下

https://developer.android.com/reference/android/net/wifi/WifiManager#addNetwork(android.net.wifi.WifiConfiguration)

添加
wifiManager.addNetwork
使能
wifiManager.enableNetwork
移除,此处如果是设置里面原有保存的,则无法移除,需要引导
wifiManager.removeNetwork

AndroidQ 以后:

https://developer.android.com/guide/topics/connectivity/wifi-suggest?hl=zh-cn

/**
 * Android API 29 之后的wifi连接验证
 * @param ssid  账号
 * @param pwd   密码, 目前都是用 WAP2 方式
 */
@RequiresApi(api = Build.VERSION_CODES.Q)
private void connectWifiAfterQ(String ssid, String pwd){
    WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder();
    builder.setSsid(ssid);
    builder.setWpa2Passphrase(pwd);

    WifiNetworkSpecifier wifiNetworkSpecifier = builder.build();

    NetworkRequest.Builder networkRequestBuilder1 = new NetworkRequest.Builder();
    networkRequestBuilder1.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
    //networkRequestBuilder1.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        networkRequestBuilder1.setNetworkSpecifier(wifiNetworkSpecifier);
    }

    NetworkRequest networkRequest = networkRequestBuilder1.build();
    ConnectivityManager cm = (ConnectivityManager)
            getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);

    // 自己独有的callback中响应事件
    ConnectivityManager.NetworkCallback networkCallback = new
            ConnectivityManager.NetworkCallback() {
                @Override
                public void onAvailable(Network network) {
                    super.onAvailable(network);
                    Log.d(TAG, "onAvailable:" + network);
                    //让本App能够使用到此网络,此时系统其他应用是无法联网的,不知道是不是bug
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        cm.bindProcessToNetwork(network);
                    }
                }

                @Override
                public void onLost(Network network) {
                    Log.d(TAG, "The application no longer has a default network. The last default network was " + network);
                }

                @Override
                public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
                    Log.d(TAG, "The default network changed capabilities: " + networkCapabilities);
                }

                @Override
                public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
                    Log.d(TAG, "The default network changed link properties: " + linkProperties);
                }

                @Override
                public void onUnavailable() {
                    super.onUnavailable();
                    Log.d(TAG, "onUnavailable:");
                    EventBus.getDefault().post("onUnavailable");
                }
            };
    cm.requestNetwork(networkRequest, networkCallback);
}

这种安卓10以上版本的则需要使用suggestion方式去请求网络,但是目前调试发现一个问题就是通过App成功连接到网络之后,手机系统的其他应用则不能通过WiFi联网,而官网给出的移除网络API使用不生效:

https://developer.android.com/reference/android/net/wifi/WifiManager#removeNetworkSuggestions(java.util.List%3Candroid.net.wifi.WifiNetworkSuggestion%3E)

查找了一通资料,最后貌似好像看到说是谷歌系统的bug,至今可能还没有修复:

https://issuetracker.google.com/issues/140398818/resources

在国内的华米OV几大机型上都测试了,具有系统其他应用不能通过WIFI上网的问题,由此影响用户体验,所以最终不进行网络连接测试,改为上报wifi名称和密码,由设备自己去验证网络连接,然后在APP中展示联网效果,由此来看,其他的IoT设备,例如百度音响,是否也是这么实现,并没有通过App来改变手机本身系统的WiFi连接。

但是安卓10以下版本是无此问题的。

参考实现demo:

https://github.com/zly394/WifiListDemo

https://github.com/lilongweidev/Android13Wifi

三、苹果端实现

​​​​​​https://developer.apple.com/documentation/networkextension/wi-fi_configuration/

刚开始看文档,以为会很简单,调用几个接口即可实现。

 后来才知道这个所谓的热点助手才能实现获取列表功能,而且这个接口的使用权限要单独申请:
https://developer.apple.com/contact/request/hotspot-helper/

不出意外,这个申请果然被拒了,苹果认为只有运营商或者网络设备制造商才有需要此功能。

Thank you for your interest in the NEHotspotHelper API. This API is not designed for the use you’ve identified, so this request cannot be approved.

The NEHotspotHelper API is meant to be used by hotspot network implementers to connect their users to the internet via a large aggregated network of Wi-Fi Hotspots that they manage.

NEHotspotHelper was designed to facilitate internet hotspot network connections and is not appropriate for apps trying to do IoT accessory integration, Wi-Fi location, or other low-level Wi-Fi tasks like signal strength. Specifically, NEHotspotHelper does not let your app initiate a local Wi-Fi scan, or access iOS’ internal list of nearby SSIDs.  

Many perceived uses of NEHotspotHelper, such as the configuration of an IoT accessory, can be accomplished with NEHotspotConfiguration, which does not require an Apple-approved entitlement.

For information about enabling your app to configure an IoT accessory, please see the following article: Configuring a Wi-Fi Accessory to Join the User’s Network

For a complete explanation of Wi-Fi management APIs on iOS, please see Technote TN3111: iOS Wi-Fi API Overview.

For further technical assistance please visit the Apple Developer Forums.

Thank You,

Apple Developer Relations

于是只能退而求其次,通过用户手动输入WiFi名称和密码,App端来进行网络连接校验,这其中也遇到了权限问题:

-(NSString *)getCurrentWifi{
    NSString * wifiName = @"";
    CFArrayRef wifiInterfaces = CNCopySupportedInterfaces();
    if (!wifiInterfaces) {
        wifiName = @"";
    }
    NSArray *interfaces = (__bridge NSArray *)wifiInterfaces;
    for (NSString *interfaceName in interfaces) {
        CFBridgingRetain(interfaceName);
        CFDictionaryRef dictRef = CNCopyCurrentNetworkInfo((__bridge CFStringRef)(interfaceName));
        if (dictRef) {
            NSDictionary *networkInfo = (__bridge NSDictionary *)dictRef;
            wifiName = [networkInfo objectForKey:(__bridge NSString *)kCNNetworkInfoKeySSID];
            NSLog(@"接口查询当前连接的wifi名称为: %@", wifiName);
            CFRelease(dictRef);
        }
    }
    CFRelease(wifiInterfaces);
    NSLog(@"最终确认当前连接的wifi名称为: %@", wifiName);
    return wifiName;
}
 

就是如上这段代码刚开始始终是获取不到当前连接的WiFi名称的,而网络上查询的资料大部分都是用此方法。后面无意中在发现一个告警日志:


sent invalid result code [1] for Wi-Fi information request

通过网络搜索,原来需要开启capacity Access wifi information .

而不只是引入这两个框架即可:

//连接wifi的框架
#import <NetworkExtension/NetworkExtension.h>
//获取当前wifi的框架
#import <SystemConfiguration/CaptiveNetwork.h>

并且这个方法单独调用也是获取不到当前Wifi名称的,而是需要NEHotspotConfigurationManager 这个请求回调里面执行才可。(不知道是不是也只能查询到自己发起请求连接的WiFi,还是怎么回事)

 NEHotspotConfiguration * configuration = [[NEHotspotConfiguration alloc] initWithSSID:wifiName passphrase:passwd isWEP:NO];
    [[NEHotspotConfigurationManager sharedManager] applyConfiguration:configuration completionHandler:^(NSError * _Nullable error) {
        
        if(error != nil){
            NSLog(@"apply config error=%@ code=%ld", error.description, (long)error.code);
        }else{
            NSLog(@"apply config success ? wifiName=%@, passwd=%@", wifiName, passwd);
        }
        //有时无法加入WIFI,没有返回error
        if ([[self getCurrentWifi] isEqualToString:wifiName]) {
            if (error) {
                //无法加入网络,需移除
                [[NEHotspotConfigurationManager sharedManager] removeConfigurationForSSID:wifiName];
                if(error.code == NEHotspotConfigurationErrorAlreadyAssociated){
                    //上报账号密码,这个是正确的连接, 应该不会出现这个逻辑了
                    NSLog(@"WiFi之前已经连接成功,不应该走这个逻辑了,前面每次都先移除了");
                    //[self ConfigSuccessBack];
                }else{
                    [self alertConfigInfoError:NSLocalizedString(@"wifiConfigAccountPasswordError", nil)];
                }
                
            }else{
                //连接wifi成功
                NSLog(@"连接WiFi成功");
                [self ConfigSuccessBack];
            }
        }else{
            //无法加入网络,需移除
            [[NEHotspotConfigurationManager sharedManager] removeConfigurationForSSID:wifiName];
            [self alertConfigInfoError:NSLocalizedString(@"wifiConfigAccountPasswordError", nil)];
        }
    }];
 

总之一番操作之后,确认需要添加的Capacity 有如下三个:

+       com.apple.developer.networking.HotspotConfiguration

+       com.apple.developer.networking.networkextension

+       com.apple.developer.networking.wifi-info

最终苹果端这边还是可以成功验证网络是否正常连接,而且不影响系统重其他应用上网。

一个看似很简单的功能,前前后后,各种零散问题分析查阅调试验证,安卓的各种机型和版本,苹果的各种权限和邮件回复,让这个功能还是弄了蛮久,关键是这种功能涉及敏感安全,为了防止滥用,系统平台随着自己的不断完善发展,对其限制要求会越来越严格。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/798601.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot——内置数据库

简单介绍 关于数据层的三大组件&#xff0c;数据源&#xff0c;持久化技术&#xff0c;数据库。前两种都已经介绍过了SpringBoot的内置的解决方案&#xff0c;还有最后一个数据库&#xff0c;在SpringBoot中&#xff0c;内置了三款数据库。分别是&#xff1a; H2HSQLDerby 这…

ARTIF:一种先进的实时威胁智能识别框架

关于ARTIF ARTIF是一个新型的高级实时威胁智能框架&#xff0c;它基于MISP并添加了另一个抽象层&#xff0c;以实现根据IP地址和历史数据识别恶意Web流量。除此之外&#xff0c;该工具还可以通过收集、处理和关联基于不同因素的观测值来执行自动分析和威胁评分。 功能介绍 评…

uni-app在小米手机上运行【步骤细节】

注意细节重点&#xff1a; 1.手机使用数据线与电脑连接&#xff0c;手机连接模式必须是传输文件模式 2.手机必须打开开发者模式 3.打开开发者模式后&#xff0c;仔细浏览并调整USB调试权限&#xff0c;重点打开USB是否允许安装按钮&#xff01;&#xff01;&#xff01; 操作步…

onnxruntime (C++/CUDA) 编译安装

一、克隆及编译 git clone --recursive https://github.com/Microsoft/onnxruntime cd onnxruntime/ git checkout v1.8.0如果克隆的时候报错&#xff1a; 执行以下&#xff1a; apt-get install gnutls-bin git config --global http.sslVerify false git config --global h…

自动化测试的技术路线

本文中我谈一下自动化测试的技术路线&#xff0c;同时也是测试团队的发展路线。 团队路线1.工程化路线 如果你们公司只有你一个测试&#xff0c;那工程化的路线是必然的选择。另外&#xff0c;我个人比较推崇工程化而非平台化。 下图介绍了工程化路线下写出来的自动化测试&…

[oeasy]python0075_删除变量_del_delete_variable

删除变量 回忆上次内容 上次我们研究了字节序 字节序有两种 符号英文名称中文名称<little-endian小字节序>big-endian大字节序 字节序 用来 明确 整型数字存储的 顺序 如果 读写数字出了错 可以 考虑一下 是否 字节序出了问题 变量现在可以 声明初始化存储了 但是 …

Animator Animator Controller Avatar relationship

Animator 组件用于将动画分配给场景中的游戏对象。Animator 需要对Animator Controller 的引用&#xff0c;该控制器定义要使用的动画剪辑&#xff0c;并控制何时以及如何在它们之间混合和过渡。 如果 GameObject 是具有 Avatar 定义的人形角色&#xff0c;则 Avatar 也应在此组…

【数据结构】--189.轮转数组

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

Python GUI 案例 (tkinter module)

Python GUI 设计案例 基于matplotlib的 y-x 简单绘图 import tkinter as tk from tkinter import filedialog import matplotlib.pyplot as plt import numpy as npdef select_x_file():x_file_path filedialog.askopenfilename(title"x")x_file_entry.delete(0, t…

Pytorch学习笔记 | 数据类型 | mnist数据集

数据类型 python中数据类型和pytorch中的对应关系 注意:pytorch是没有没有string类型的 例1:创建一个3行4列的随机数数组,符合均值为0,方差为1的正态分布 import torch a=torch.Tensor(3,4) a Out[17]: tensor([[0.

iperf3跑满100G网卡实测记录

环境准备 拓扑 两台服务器 100G网卡对插直连。 ubuntu20.04, 系统设置默认状态 MTU 1500 网卡 Mellonax ConnectX5 100G rootvnet:~# lspci | grep Mellanox 98:00.0 Ethernet controller: Mellanox Technologies MT27800 Family [ConnectX-5] 98:00.1 Ethernet control…

【高级数据结构】树状数组

目录 树状数组1 &#xff08;单点修改&#xff0c;区间查询&#xff09; 树状数组1 &#xff08;单点修改&#xff0c;区间查询&#xff09; 洛谷&#xff1a;树状数组1https://www.luogu.com.cn/problem/P3374 题目描述 如题&#xff0c;已知一个数列&#xff0c;你需要进行…

汇报的目标和技巧【研发版】

汇报的组织意义 要做好汇报&#xff0c;需要先理解汇报的意义&#xff0c;这是根源上的指导。直接地说&#xff0c;满足上级的需求就是汇报的价值&#xff0c;而上级需求来自企业运作的需要&#xff0c;这也是各级管理者职责的组成部分。 各级管理者的职责&#xff08;倒序引用…

API声明文件Swagger Injection攻击

Swagger 在API化的世界里&#xff0c;相信无论是前端还是后端开发&#xff0c;都或多或少地被接口维护折磨过。随着API迭代&#xff0c;老旧API文档和SDK需要更新&#xff0c;这是一个耗散研发精力的事情。为解决此类问题&#xff0c;以API为软件能力最终交付物的生态不断演进…

实训笔记7.27

实训笔记7.27 7.27笔记一、Hive数据仓库基本概念&#xff08;处理结构化数据&#xff09;1.1 Hive的组成架构1.1.1 Hive的客户端1.1.2 Hive的驱动程序1.1.3 Hive的元数据库 1.2 Hive和数据库的区别 二、Hive的安装配置三、Hive的相关配置项四、Hive的基本使用方式4.1 使用Hive的…

FS32K144官方提供串口Bootloader对接Matlab串口烧写程序

​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 前言 Bootloader升级工具&#xff1a;可用TTL、232、485&#xff08;硬件收发模式&#xff09;,其中的一种&#x…

DP学习第四篇之不同路径II

DP学习第四篇之不同路径|| 63. 不同路径 II - 力扣&#xff08;LeetCode&#xff09; 一.题目解析 二. 算法原理 状态表示 tips: 经验题目要求。以[i,j]位置为结尾&#xff0c;。。。 dp[i][j]: 走到[i, j]位置时&#xff0c;一共多少种路径 状态转移方程 tips: 用之前或…

【业务功能篇55】Springboot+easyPOI 导入导出

Apache POI是Apache软件基金会的开源项目&#xff0c;POI提供API给Java程序对Microsoft Office格式档案读和写的功能。 Apache POI 代码实现复杂&#xff0c;学习成本较高。 Easypoi 功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员 就可以方便的写出Excel导出…

Golang单元测试详解:单元测试的基本使用方法

Golang 单元测试 Golang 中的单元测试是使用标准库 testing 来实现的&#xff0c;编写一个单元测试是很容易的&#xff1a; 创建测试文件&#xff1a;在 Go 项目的源代码目录下创建一个新的文件(和被测代码文件在同一个包)&#xff0c;以 _test.go 为后缀名。例如&#xff0c…

性能优化 - 前端性能监控和性能指标计算方式

性能优化 - 前端性能监控和性能指标计算方式 前言一. 性能指标介绍1.1 单一指标介绍1.2 指标计算① Redirect(重定向耗时)② AppCache(应用程序缓存的DNS解析)③ DNS(DNS解析耗时)④ TCP(TCP连接耗时)⑤ TTFB(请求响应耗时)⑥ Trans(内容传输耗时)⑦ DOM(DOM解析耗时) 1.3 FP(f…