Android P中WifiManager.startScan()已废弃,如何每3秒获取WiFi扫描结果?
Android P+ 实现WiFi扫描结果定期获取(接近3秒间隔)
嘿,这个问题我之前做项目的时候踩过坑,给你梳理几个可行的思路,不过得先明确Android P(API 28)及更高版本的核心限制:谷歌废弃了startScan(),而且应用完全失去了主动触发WiFi扫描的能力,所有扫描都由系统根据自身策略(耗电、位置变化、网络需求等)来调度。直接调用getScanResults()拿到的是系统最近一次扫描的缓存,所以更新慢是必然的。
要尽可能接近每3秒获取一次最新结果,可以试试下面这些方案:
1. 监听系统扫描完成广播,实时获取最新结果
系统每次完成WiFi扫描后,都会发送WifiManager.SCAN_RESULTS_AVAILABLE_ACTION广播,我们可以注册这个广播接收器,在收到通知时立即拉取最新结果。
步骤:
- 先申请必要权限(Manifest里配置):
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
- 代码里注册广播接收器:
private WifiManager mWifiManager; private BroadcastReceiver mScanResultReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); // 初始化广播接收器 mScanResultReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) { // 检查扫描是否成功更新 boolean isUpdated = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false); if (isUpdated) { List<ScanResult> latestResults = mWifiManager.getScanResults(); // 这里处理你的扫描结果 processScanResults(latestResults); } else { // 扫描失败,可能是权限不足或系统限制 Log.w("WiFiScan", "扫描结果未更新"); } } } }; // 注册广播 IntentFilter filter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); registerReceiver(mScanResultReceiver, filter); } @Override protected void onDestroy() { super.onDestroy(); // 记得注销广播,避免内存泄漏 unregisterReceiver(mScanResultReceiver); } private void processScanResults(List<ScanResult> results) { // 你的业务逻辑,比如打印结果、更新UI等 }
注意:这个方案的扫描频率完全依赖系统,默认情况下系统可能十几秒甚至几十秒才扫一次,达不到3秒的要求,所以需要配合下面的方法来触发系统更频繁扫描。
2. 利用位置服务触发系统高频扫描
系统为了辅助定位,在位置变化时会主动触发WiFi扫描。我们可以通过请求高频位置更新,让系统被迫更频繁地做WiFi扫描,从而间接实现接近3秒的结果更新。
步骤:
- 确保已经申请
ACCESS_FINE_LOCATION权限(Android 10+还需要ACCESS_BACKGROUND_LOCATION才能在后台生效) - 集成Fused Location Provider请求位置更新:
private FusedLocationProviderClient mFusedLocationClient; private LocationCallback mLocationCallback; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化位置服务 mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); // 配置位置请求参数,设置3秒间隔 LocationRequest locationRequest = LocationRequest.create(); locationRequest.setInterval(3000); // 3秒一次位置更新 locationRequest.setFastestInterval(1000); // 最快1秒一次(系统可能调整) locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); // 高精度模式会触发WiFi扫描 // 位置更新回调,在这里获取WiFi扫描结果 mLocationCallback = new LocationCallback() { @Override public void onLocationResult(LocationResult locationResult) { super.onLocationResult(locationResult); // 位置更新时,系统大概率刚完成WiFi扫描,直接取最新结果 List<ScanResult> latestResults = mWifiManager.getScanResults(); processScanResults(latestResults); } }; // 开始请求位置更新 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { mFusedLocationClient.requestLocationUpdates(locationRequest, mLocationCallback, Looper.getMainLooper()); } } @Override protected void onDestroy() { super.onDestroy(); // 停止位置更新,避免耗电 mFusedLocationClient.removeLocationUpdates(mLocationCallback); }
注意:
- 这种方式依赖位置服务,用户关闭位置服务后就失效了
- 系统依然会限制扫描频率,即使你设置3秒位置间隔,也可能无法做到精确3秒一次(比如后台时系统会降低频率)
- 高频位置更新会增加耗电,要注意用户体验,最好只在前台需要时开启
3. 避坑提醒
- 绝对不要用
Timer或Handler每隔3秒调用getScanResults(),这样拿到的都是旧缓存,不会有新结果 - Android 10+后台获取扫描结果必须申请
ACCESS_BACKGROUND_LOCATION权限,否则只能在前台获取 - 系统对WiFi扫描有全局频率限制,即使你做了所有操作,也无法突破系统的耗电保护策略,所以只能尽可能接近3秒的目标
内容的提问来源于stack exchange,提问作者EAK TEAM




