WLAN 扫描流程
扫描流程分为三步:
- 由于短时间扫描过多,扫描请求可能遭到节流。
- 设备处于空闲状态,扫描已停用。
- WLAN 硬件报告扫描失败。使用 WifiManager.getScanResults() 获取扫描结果。系统返回的扫描结果为最近更新的结果,但如果当前扫描尚未完成或成功,可能会返回以前扫描的结果。也就是说,如果在收到成功的 SCAN_RESULTS_AVAILABLE_ACTION 广播前调用此方法,您可能会获得较旧的扫描结果。
以下代码提供了如何实现这些步骤的示例:
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() 需要以下任意一项权限:
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
CHANGE_WIFI_STATE
对于上述权限,如果调用应用一项都不具备,调用将会失败,并显示 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 扫描调节下)。
实际调试代码如下:
package com.mob.testwifi;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.widget.TextView;
import java.util.List;
public class MainActivity extends AppCompatActivity {
WifiManager wifiManager;
TextView tv;
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.result);
if (ContextCompat.checkSelfPermission(
MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) ==
PackageManager.PERMISSION_GRANTED) {
// You can use the API that requires the permission.
wifiManager = (WifiManager) this.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
registerReceiver(wifiScanReceiver, intentFilter);
boolean success = wifiManager.startScan();
if (!success) {
// scan failure handling
scanFailure();
}
} else {
// You can directly ask for the permission.
requestPermissions(
new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
},
1);
}
}
BroadcastReceiver wifiScanReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context c, Intent intent) {
boolean success = intent.getBooleanExtra(
WifiManager.EXTRA_RESULTS_UPDATED, false);
tv.setText("success:\n" + success);
if (success) {
scanSuccess();
} else {
// scan failure handling
scanFailure();
}
}
};
private void scanSuccess() {
List<ScanResult> results = wifiManager.getScanResults();
tv.setText(results.toString());
}
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();
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("scanFailure:\n" + results.toString());
}
});
}
}
注:
本文参考Android官方开发文档
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)