您可以使用 WifiManager API 提供的 WLAN 扫描功能来获取设备上可见的 WLAN 接入点列表。
WLAN 扫描流程
扫描流程分为三步:
为
SCAN_RESULTS_AVAILABLE_ACTION
注册一个广播监听器,系统会在完成扫描请求时调用此监听器,提供其成功/失败状态。对于搭载 Android 10(API 级别 29)及更高版本的设备,系统将针对平台或其他应用在设备上执行的所有完整 WLAN 扫描发送此广播。应用可以使用广播被动监听设备上所有扫描的完成情况,无需发出自己的扫描。使用
WifiManager.startScan()
请求扫描。请务必检查方法的返回状态,因为调用可能因以下任一原因失败:- 由于短时间扫描过多,扫描请求可能遭到节流。
- 设备处于空闲状态,扫描已停用。
- WLAN 硬件报告扫描失败。
使用
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)引入了有关权限和 WLAN 扫描允许频率的限制。
为了提高网络性能和安全性,延长电池续航时间,Android 9(API 级别 28)收紧了权限要求,并进一步限制 WLAN 扫描频率。
权限
Android 8.0 和 Android 8.1:
成功调用 WifiManager.getScanResults()
需要以下任意一项权限:
对于上述权限,如果调用应用一项都不具备,调用将会失败,并显示 SecurityException
。
或者,在搭载 Android 8.0(API 级别 26)及更高版本的设备上,您可以使用 CompanionDeviceManager
代表应用对附近的配套设备执行扫描,而不需要位置权限。有关此选项的详情,请参阅
配套设备
配对。
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 扫描调节下)。