Android 8 Oreo中WifiManager.startScan无响应问题求助
问题分析与解决方案
你的问题核心并不是startScan()方法的弃用(该方法是API28才正式标记弃用),而是Android 8.0(API26)引入的广播接收机制限制:从API26开始,系统禁止静态注册的BroadcastReceiver接收大部分隐式广播,其中就包括android.net.wifi.SCAN_RESULTS(对应WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)。这就是为什么你的代码在targetSdkVersion≤25时正常,升级到26及以上就失效的关键原因。
解决方案:改用动态注册BroadcastReceiver
你需要移除Manifest中的静态接收器注册,改为在代码中动态注册接收器,并且在不需要的时候及时注销,这样才能正常接收扫描结果广播。
步骤1:修改扫描启动代码,添加动态注册逻辑
private InOutWifiScanResultsReceiver scanReceiver; private static final String LOG_TAG = "WifiScanner"; public boolean startScan(Context context) { WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); // 检查WiFi是否开启 if (!wm.isWifiEnabled()) { try { wm.setWifiEnabled(true); } catch (SecurityException e) { Log.w(LOG_TAG, "Failed to enable WiFi", e); return false; } } // 动态注册扫描结果接收器 if (scanReceiver == null) { scanReceiver = new InOutWifiScanResultsReceiver(); IntentFilter filter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); // Android 12+适配:必须指定接收器导出状态 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { context.registerReceiver(scanReceiver, filter, Context.RECEIVER_NOT_EXPORTED); } else { context.registerReceiver(scanReceiver, filter); } } // 启动扫描 boolean scanInitiated = wm.startScan(); Log.d(LOG_TAG, "Scan initiated successfully? " + scanInitiated); return scanInitiated; } // 务必在合适的时机注销接收器(比如Activity的onDestroy、扫描完成后) public void cleanupScanner(Context context) { if (scanReceiver != null) { try { context.unregisterReceiver(scanReceiver); scanReceiver = null; } catch (IllegalArgumentException e) { Log.w(LOG_TAG, "Receiver was already unregistered", e); } } }
步骤2:移除Manifest中的静态接收器注册
删除Manifest里的这段代码:
<receiver android:name=".InOutWifiScanResultsReceiver" android:exported="false"> <intent-filter> <action android:name="android.net.wifi.SCAN_RESULTS"/> </intent-filter> </receiver>
步骤3:补充关键注意事项
- 权限与位置服务:你已经做了这部分,但需要再次确认:
- 必须申请并授予
ACCESS_FINE_LOCATION(或ACCESS_COARSE_LOCATION)权限 - 设备的位置服务必须处于开启状态(GPS或其他定位模式)
- 必须申请并授予
- Android 12+适配:如果你的
targetSdkVersion≥31,动态注册接收器时必须指定RECEIVER_NOT_EXPORTED(或RECEIVER_EXPORTED,如果需要外部触发),否则会抛出异常 - 系统扫描节流:系统会限制扫描频率,短时间内多次调用
startScan()可能被系统忽略,避免频繁触发扫描 - 内存泄漏预防:务必在组件(如Activity、Service)销毁前注销接收器,避免内存泄漏
提前适配API28+的替代方案
虽然你的问题聚焦在API26,但可以提前了解:从Android 9(API28)开始,startScan()被正式弃用,官方推荐使用WifiManager.startScan(List<ScanSpec>, Executor, ScanCallback)或WifiNetworkSpecifier进行WiFi扫描。不过对于API26-27,上述动态注册接收器的方案依然有效。
内容的提问来源于stack exchange,提问作者Antonio




