Wifi扫描

  • 概述
  • 限制
    • 权限
    • 节流
  • 实现
  • 总结

概述

 WIFI就是一种无线联网技术,常见的是使用无线路由器。那么在这个无线路由器的信号覆盖的范围内都可以采用WIFI连接的方式进行联网。如果无线路由器连接了一个ADSL线路或其他的联网线路,则又被称为“热点”。
在实际使用中,我们进入wifi列表后可以看到下面这样

 那wifi列表里面的数据是怎么来的?这里就是本文章主要讲的内容wifi如何进行扫描以及如何去更新数据。

限制

权限

 在我们开始使用前需要在AndroidManifest.xml中加入wifi相关的权限:

 //用于扫描结束后读取wifi信息<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />//用于扫描WiFi列表<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

Android 8.0 & 8.1
 成功调用WifiManager.getScanResults() 方法必须具备以下权限之一,否则会抛异常SecurityException。或者,在搭载 Android 8.0(API 级别 26)及更高版本的设备上,您可以使用 CompanionDeviceManager 代表应用对附近的配套设备执行扫描,而不需要位置权限。

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

 实际从Android6.0以上调用getScanResults()获取WiFi列表,就必须要打开GPS才能获取,否则为空。是因为6.0之前,APP在不开启GPS定位的情况下,依然可以调用getScanResults获取周边Wifi的相关信息,比如SSID、BSSID、level等,去请求谷歌位置服务器(“http://www.google.com/loc/json”),获取用户所在的位置信息;这显得GPS定位开关毫无意义,显然不合理,是位置权限设计的Bug;Google出于设计考虑,6.0之后版本,获取WiFi列表必须要设备开启了GPS定位,并且APP具备位置权限,才能获取WiFi列表。

Android 9
 成功调用 WifiManager.startScan() 需要满足以下所有条件:

  • 应用拥有 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 权限。
  • 应用拥有 CHANGE_WIFI_STATE 权限。
  • 设备已启用位置信息服务(位于设置 > 位置信息下)。

Android 10(API 级别 29)及更高版本:

 成功调用 WifiManager.startScan() 需要满足以下所有条件:

  • 如果您的应用以 Android 10(API 级别 29)SDK 或更高版本为目标平台,应用需要拥有
    ACCESS_FINE_LOCATION 权限。
  • 如果您的应用以低于 Android 10(API 级别 29)的 SDK 为目标平台,应用需要拥有
    ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION 权限。
  • 应用拥有 CHANGE_WIFI_STATE 权限。
  • 设备已启用位置信息服务(位于设置 > 位置信息下)。

 若要成功调用 WifiManager.getScanResults(),请确保满足以下所有条件:

  • 如果您的应用以 Android 10(API 级别 29)SDK 或更高版本为目标平台,应用需要拥有
    ACCESS_FINE_LOCATION 权限。
  • 如果您的应用以低于 Android 10(API 级别 29)的 SDK 为目标平台,应用需要拥有
    ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION 权限。
  • 应用拥有 ACCESS_WIFI_STATE 权限。
  • 设备已启用位置信息服务(位于设置 > 位置信息下)。

 如果调用应用无法满足上述所有要求,调用将失败,并显示 SecurityException。

节流

 使用 WifiManager.startScan() 扫描的频率适用以下限制。

Android 8.0 和 Android 8.1:

 每个后台应用可以在 30 分钟内扫描一次。

Android 9:

 每个前台应用可以在 2 分钟内扫描四次。这样便可在短时间内进行多次扫描。

 所有后台应用总共可以在 30 分钟内扫描一次。

Android 10 及更高版本:

 适用 Android 9 的节流限制。新增一个开发者选项,用户可以关闭节流功能以便进行本地测试(位于开发者选项 > 网络 > WLAN 扫描调节下)。

实现

扫描流程分为三步:

  1. 为 SCAN_RESULTS_AVAILABLE_ACTION注册一个广播监听器,系统会在完成扫描请求时调用此监听器,提供其成功/失败状态。对于搭载 Android 10(API 级别29)及更高版本的设备,系统将针对平台或其他应用在设备上执行的所有完整 WLAN扫描发送此广播。应用可以使用广播被动监听设备上所有扫描的完成情况,无需发出自己的扫描。
  2. 使用 WifiManager.startScan() 请求扫描。请务必检查方法的返回状态,因为调用可能因以下任一原因失败:
     由于短时间扫描过多,扫描请求可能遭到节流。
     设备处于空闲状态,扫描已停用。
     WLAN 硬件报告扫描失败。
  3. 使用 WifiManager.getScanResults() 获取扫描结果。系统返回的扫描结果为最近更新的结果,但如果当前扫描尚未完成或成功,可能会返回以前扫描的结果。也就是说,如果在收到成功的 SCAN_RESULTS_AVAILABLE_ACTION 广播前调用此方法,您可能会获得较旧的扫描结果。

下面看代码部分的实现,主要代码如下

    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);......//注册监听initListener();//在活动创建时开始扫描WiFiwifiManager.startScan();}private void initListener() {//注册相关广播的监听IntentFilter scanFilter = new IntentFilter();scanFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);if (wifiListReceiver == null) {wifiListReceiver = new WifiListReceiver();}registerReceiver(wifiListReceiver, scanFilter);......}private class WifiListReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();//接收到扫描完成的广播if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {//开始过滤数据,筛选有效数据。getRealWifi();Message message = Message.obtain();message.what = MSG_SCAN_WIFI;//因为Wifi的信号是实时变化的,这里我们在拿到上一个数据后再过10s开始下一次的扫描wifiHandler.sendMessageDelayed(message, 10000);}}}public void getRealWifi() {//获取wifi扫描结果wifiList = wifiManager.getScanResults();if (wifiList.size() == 0) {return;}//新建一个list存储有效wifi,有些WiFi是无效的需要过滤if (realWifiList == null) {realWifiList = new ArrayList();} else {realWifiList.clear();}for (ScanResult result : wifiList) {if (result.SSID == null || result.SSID.length() == 0 || result.capabilities.contains("[IBSS]")) {continue;}boolean found = false;for (ScanResult item : realWifiList) {if (item.SSID.equals(result.SSID) && item.capabilities.equals(result.capabilities)) {found = true;break;}}if (!found) {realWifiList.add(result);}}if (realWifiList.size() != 0) {currentWifiInfo = wifiManager.getConnectionInfo();if (wifiAdapter == null) {//第一次扫描列表wifiAdapter = new MyAdapter(WifiActivity.this, realWifiList);wifiListView.setAdapter(wifiAdapter);} else {//更新列表数据wifiAdapter.list = realWifiList;wifiAdapter.notifyDataSetChanged();}}}//负责相关消息处理private static class WifiHandler extends Handler {final WeakReference<WifiActivity> mActivity;private WifiHandler(WifiActivity activity) {mActivity = new WeakReference<WifiActivity>(activity);}@Overridepublic void handleMessage(Message msg) {WifiActivity currentActivity = mActivity.get();if (currentActivity == null) {return;}//开始下一次的扫描switch (msg.what) {case MSG_SCAN_WIFI:currentActivity.wifiManager.startScan();break;}}}

 通过以上几个方法我们就可以拿到扫描到的有效WiFi数据。然后我们就可以通过Listview或Recyclerview去加载数据。这里主要看下加载布局的部分:

public class MyAdapter extends BaseAdapter {LayoutInflater inflater;List<ScanResult> list;......@Overridepublic View getView(int position, View convertView, ViewGroup parent) {View view = null;VIewHolder viewHolder;ScanResult scanResult = getItem(position);if (convertView == null) {view = LayoutInflater.from(getBaseContext()).inflate(R.layout.item_wifi_list, parent, false);viewHolder = new VIewHolder();viewHolder.textView = (TextView) view.findViewById(R.id.item_title);viewHolder.keyType = (TextView) view.findViewById(R.id.item_key_type);viewHolder.wifiState = (TextView) view.findViewById(R.id.item_state);viewHolder.tag = (TextView) view.findViewById(R.id.tag);viewHolder.imageView = (ImageView) view.findViewById(R.id.item_level);// 将 viewHolder 存储在 View 中view.setTag(viewHolder);} else {view = convertView;// 重新获取 viewHolderviewHolder = (VIewHolder) view.getTag();}viewHolder.textView.setText(scanResult.SSID);viewHolder.keyType.setText(WifiUtil.getEncrypt(scanResult));//判断信号强度,显示对应的指示图标,数值越小代表信号越高。if (Math.abs(scanResult.level) > 100) {viewHolder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.stat_wifi_signal_1));} else if (Math.abs(scanResult.level) > 80) {viewHolder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.stat_wifi_signal_2));} else if (Math.abs(scanResult.level) > 70) {viewHolder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.stat_wifi_signal_3));} else if (Math.abs(scanResult.level) > 60) {viewHolder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.stat_wifi_signal_4));} else {viewHolder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.stat_wifi_signal_5));}return view;}class VIewHolder {TextView textView;TextView keyType;TextView wifiState;TextView tag;ImageView imageView;}}

item_wifi_list.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="@dimen/x300"android:layout_height="wrap_content"xmlns:app="http://schemas.android.com/apk/res-auto"><TextViewandroid:id="@+id/item_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="test"android:textSize="@dimen/x16"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent"/><TextViewandroid:id="@+id/item_key_type"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="NONE"android:textSize="@dimen/x16"android:layout_marginTop="@dimen/x3"app:layout_constraintLeft_toLeftOf="@+id/item_title"app:layout_constraintTop_toBottomOf="@+id/item_title"/><TextViewandroid:id="@+id/tag"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="@dimen/x45"android:visibility="gone"android:text="|"android:textSize="@dimen/x18"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintBaseline_toBaselineOf="@+id/item_key_type"/><androidx.constraintlayout.widget.Barrierandroid:id="@+id/item_barrier"android:layout_width="wrap_content"android:layout_height="wrap_content"app:barrierDirection="right"app:constraint_referenced_ids="item_title,item_key_type"/><TextViewandroid:id="@+id/item_state"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text=""android:textSize="@dimen/x16"android:layout_marginStart="@dimen/x60"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintBaseline_toBaselineOf="@+id/item_key_type"/><ImageViewandroid:id="@+id/item_level"android:layout_width="@dimen/x32"android:layout_height="@dimen/x32"android:src="@drawable/ic_back"android:layout_marginRight="@dimen/x20"app:layout_constraintTop_toTopOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintBottom_toBottomOf="parent"/></androidx.constraintlayout.widget.ConstraintLayout>

总结

 Wifi扫描的代码流程上不难,主要是权限方面的问题。保证权限申请正确的前提下以上流程就可以扫描到正确的WiFi.
 PS:本文是在Android TV上开发测试的,安卓模拟器可能无法扫描。建议真机模拟实验,另各家厂商的手机可能有修改本文的方法不一定生效。

如果想要了解更多,请参考官方文档。

AndroidTV Wifi开发(一)相关推荐

  1. Android WiFi开发教程(三)——WiFi热点数据传输

    在上一篇文章中介绍了WiFi的搜索和连接,如果你还没阅读过,建议先阅读上一篇Android WiFi开发教程(二)--WiFi的搜索和连接.本篇接着简单介绍手机上如何通过WiFi热点进行数据传输. 跟 ...

  2. Android中WIFI开发总结(一)

    WIFI就是一种无线联网技术,常见的是使用无线路由器.那么在这个无线路由器的信号覆盖的范围内都可以采用WIFI连接的方式进行联网.如果无线路由器连接了一个ADSL线路或其他的联网线路,则又被称为&qu ...

  3. android Wifi开发相关内容

    今天,简单讲讲android里如何使用WifiManager. 之前,我看代码时,看到了wifi相关的代码,发现自己对于这个内容的使用还很不熟悉,所以在网上查找资料,最终解决了问题.这里记录一下. 移 ...

  4. linux wifi开发书籍,Android WIFI开发介绍.pdf

    Android WIFI开发介绍: WifiStateTracker 会创建WifiMonitor 接收来自底层的事件,WifiService 和WifiMonitor 是整个模块的核心.WifiSe ...

  5. Android WiFi开发

    概述 介绍Android WiFi的扫描.连接.信息.以及WiFi热点等等的实现,并用代码实现. 详细 代码下载:http://www.demodashi.com/demo/10660.html 一. ...

  6. android获取wifi开关,Android WiFi开发(一)--WiFi开关与状态监听

    之前开发了一个WiFi,热点相关的应用.因为对这方面也不熟悉,刚开始找资料看书,但看明白实现时,发现随着android版本更新,相关api有较大改动,之前的代码不能用.经过一番探索,最后实现出来了,现 ...

  7. WiFi开发|WiFi无线技术介绍

    WiFi无线技术介绍 1. WiFi技术概述 WLAN是无线局域网络的简称,全称为Wireless Local Area Networks,是一种利用无线技术进行数据传输的系统,该技术的出现能够弥补有 ...

  8. 基于Wio RP2040迷你无线WiFi开发板的硬件接口技术及MicroPython控制编程基础

    Wio RP2040迷你无线WiFi开发板(Wio RP2040 mini Dev Board)是Seeed Studio公司于2021年5月推出的一款迷你无线WiFi开发板,它集成了Wio RP20 ...

  9. 树莓派Pico W无线WiFi开发板使用方法及MicroPython网络编程实践

    树莓派Pico W开发板是树莓派基金会于2022年6月底推出的一款无线WiFi开发板,它支持C/C++和MicroPython编程.本文介绍树莓派Pico W无线WiFi开发板的使用方法及MicroP ...

最新文章

  1. 计算机也可以看“视频”,理解“视频”
  2. 学习dos批处理,再也不怕老板安排一些重复性高的工作了,几行代码就搞定!
  3. 【OS】期末总结复习
  4. LeetCode 之 JavaScript 解答第141题 —— 环形链表 I(Linked List Cycle I)
  5. SAP UI5 Nav container - how the inner control is added
  6. pythrch 启动 visdom可视化
  7. 2021年中国乙烯基一次性手套市场趋势报告、技术动态创新及2027年市场预测
  8. PAT 1084. 外观数列 (20) - 乙级
  9. Git基础教程(三)
  10. python socket编程(tcp/udp)
  11. 如何应对当下的 996?
  12. kindle刷机ttl_亚马逊卡大树kindle voyage修复刷机救砖KV死机变砖忘记密码维修
  13. SIAMfc++:采用目标估计准则,实现稳健和准确的视觉跟踪
  14. c语言单片机矩阵键盘,51单片机矩阵键盘的C语言程序与分析
  15. Android__逆向__xpose使用
  16. Oracle 误删恢复
  17. 回顾总结-----第九届中国云计算大会,量子计算机为最大亮点
  18. 与Anthony Baldino一起塑造声音
  19. 凡科小程序服务器域名,小程序支付申请及配置教程
  20. Lua语言实现游戏动作

热门文章

  1. Anaconda在开始菜单找不到Anaconda command prompt入口
  2. 2017广东工业大学程序设计竞赛决赛
  3. 数据运营系列(三):熵权法如何确定指标权重构建评价体系
  4. 手机畅游软件-Java手机版2.0
  5. 把数组排成最小的数字,划分字母区间,最小覆盖子串,验证回文字符串II
  6. WebSecurity 类
  7. Banana Pi BPI-6202 开源嵌入式单板工控机,支持HMI及全功能SCADA功能
  8. 全景图(panorama)低成本解决方案
  9. Android送花动画
  10. COGS896圈奶牛