You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动