Wi-Fi 扫描概述

您可以使用 WifiManager API 提供的 Wi-Fi 扫描功能来获取设备可见的 Wi-Fi 接入点列表。

Wi-Fi 扫描流程

扫描流程分为三个步骤

  1. 注册广播监听器,用于 SCAN_RESULTS_AVAILABLE_ACTION,该监听器在扫描请求完成后调用,提供其成功/失败状态。对于运行 Android 10(API 级别 29)及更高版本的设备,此广播将针对平台或其他应用在设备上执行的任何完整 Wi-Fi 扫描发送。应用可以通过使用广播而无需发出自己的扫描来被动地监听设备上的所有扫描完成情况。

  2. 请求扫描,使用 WifiManager.startScan()。请务必检查方法的返回状态,因为调用可能由于以下任何原因而失败

    • 由于短时间内扫描次数过多,扫描请求可能会被限制。
    • 设备处于空闲状态且扫描已禁用。
    • Wi-Fi 硬件报告扫描失败。
  3. 获取扫描结果,使用 WifiManager.getScanResults()。返回的扫描结果是最新的结果,如果您的当前扫描尚未完成或成功,则可能是来自先前扫描的结果。这意味着,如果在收到成功的 SCAN_RESULTS_AVAILABLE_ACTION 广播之前调用此方法,您可能会获得较旧的扫描结果。

以下代码提供了如何实现这些步骤的示例

Kotlin

val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager

val wifiScanReceiver = object : BroadcastReceiver() {

  override fun onReceive(context: Context, intent: Intent) {
    val success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)
    if (success) {
      scanSuccess()
    } else {
      scanFailure()
    }
  }
}

val intentFilter = IntentFilter()
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
context.registerReceiver(wifiScanReceiver, intentFilter)

val success = wifiManager.startScan()
if (!success) {
  // scan failure handling
  scanFailure()
}

....

private fun scanSuccess() {
  val results = wifiManager.scanResults
  ... use new scan results ...
}

private fun scanFailure() {
  // handle failure: new scan did NOT succeed
  // consider using old scan results: these are the OLD results!
  val results = wifiManager.scanResults
  ... potentially use older scan results ...
}

Java

WifiManager wifiManager = (WifiManager)
                   context.getSystemService(Context.WIFI_SERVICE);

BroadcastReceiver wifiScanReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context c, Intent intent) {
    boolean success = intent.getBooleanExtra(
                       WifiManager.EXTRA_RESULTS_UPDATED, false);
    if (success) {
      scanSuccess();
    } else {
      // scan failure handling
      scanFailure();
    }
  }
};

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
context.registerReceiver(wifiScanReceiver, intentFilter);

boolean success = wifiManager.startScan();
if (!success) {
  // scan failure handling
  scanFailure();
}

....

private void scanSuccess() {
  List<ScanResult> results = wifiManager.getScanResults();
  ... use new scan results ...
}

private void scanFailure() {
  // handle failure: new scan did NOT succeed
  // consider using old scan results: these are the OLD results!
  List<ScanResult> results = wifiManager.getScanResults();
  ... potentially use older scan results ...
}

限制

Android 8.0(API 级别 26)引入了有关权限和允许的 Wi-Fi 扫描频率的限制。

为了提高网络性能、安全性以及电池续航时间,Android 9(API 级别 28)加强了权限要求,并进一步限制了 Wi-Fi 扫描的频率。

权限

Android 8.0 和 Android 8.1

成功调用 WifiManager.getScanResults() 需要满足以下权限中的**任意一个**

如果调用应用程序没有这些权限中的任何一个,则调用会失败并抛出 SecurityException

或者,在运行 Android 8.0(API 级别 26)及更高版本的设备上,您可以使用 CompanionDeviceManager 代表您的应用程序扫描附近的伴侣设备,而无需位置权限。有关此选项的更多信息,请参阅 伴侣设备配对

Android 9

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

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

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

  • 如果您的应用程序的目标是 Android 10(API 级别 29)SDK 或更高版本,则您的应用程序必须具有 ACCESS_FINE_LOCATION 权限。
  • 如果您的应用程序的目标 SDK 低于 Android 10(API 级别 29),则您的应用程序必须具有 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION 权限。
  • 您的应用程序具有 CHANGE_WIFI_STATE 权限。
  • 设备上已启用位置服务(在**设置 > 位置**下)。

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

  • 如果您的应用程序的目标是 Android 10(API 级别 29)SDK 或更高版本,则您的应用程序必须具有 ACCESS_FINE_LOCATION 权限。
  • 如果您的应用程序的目标 SDK 低于 Android 10(API 级别 29),则您的应用程序必须具有 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION 权限。
  • 您的应用程序具有 ACCESS_WIFI_STATE 权限。
  • 设备上已启用位置服务(在**设置 > 位置**下)。

如果调用应用程序不满足所有这些要求,则调用会失败并抛出 SecurityException

限制

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

Android 8.0 和 Android 8.1

每个后台应用程序在 30 分钟内只能扫描一次。

Android 9

每个前台应用程序在 2 分钟内可以扫描四次。这允许在短时间内进行突发扫描。

所有后台应用程序组合在 30 分钟内只能扫描一次。

Android 10 及更高版本

与 Android 9 相同的限制适用。有一个新的开发者选项可以切换限制以进行本地测试(在**开发者选项 > 网络 > Wi-Fi 扫描限制**下)。