You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 1大意模拟规范 risks筛选 enum Avoid...依班 这条 (修正为)Android 13下BluetoothLeScanner无扫描结果,AltBeacon触发回调求助

问题概述
  • 测试设备:三星M33 5G
  • 系统版本:Android 13
  • 核心问题:使用Android原生BluetoothLeScanner初始化扫描时,注册的ScanCallback完全收不到扫描结果,但相同代码在Android 13以下版本运行正常。已确认授予「查找附近设备」ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION等所有必要权限,排除权限问题。
  • 特殊现象:当在应用其他类中初始化并运行第三方库AltBeacon的BeaconManager后,原本无法收到回调的BluetoothLeScanner突然能正常获取扫描结果,二者运行在不同线程,不清楚AltBeacon是如何触发系统让所有BluetoothLeScanner回调生效的。
相关代码
public class BLEScanner {

    private BluetoothLeScanner Scanner;
    private BleAdapter adapter;
    private Timer RemoveTimer;
    private Context ctx;
    private Handler mHandler = new Handler(Looper.getMainLooper());
    private Timer mTimer;
    private boolean isSortingScheduled = false;

    public BLEScanner(Context mctx, BleAdapter ade) throws Exception {
        ctx = mctx;
        adapter = ade;

        ResumeScanning();
    }

    public void PauseScanning() {
        try {
            if (Scanner != null) {
                Scanner.stopScan(mScanCallback);
                Scanner = null;
            }

        } catch (SecurityException ex) {
            Log.e("BLEScanner", ex.getMessage());
        }
    }

    public void ResumeScanning() throws Exception {
        final BluetoothManager bluetoothManager =
                (BluetoothManager) ctx.getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE);
        BluetoothAdapter adepter = BluetoothAdapter.getDefaultAdapter();
        if (adepter != null) {
            Scanner = null;
            Scanner = adepter.getBluetoothLeScanner();

            if (Scanner != null) {
                try {
                    // 创建扫描过滤器
                    ScanSettings settings = new ScanSettings.Builder()
                            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                            .build();
                    ArrayList<ScanFilter> filters = new ArrayList<ScanFilter>();
                    ScanFilter filter1 = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString("XXXXXXXX-0000-1000-8000-0080XXXXXXXX")).build();
                    filters.add(filter1);
                    Scanner.startScan(filters, settings, mScanCallback);
                    if (RemoveTimer != null) {
                        RemoveTimer.cancel();
                        RemoveTimer = null;
                    }
                    RemoveTimer = new Timer();
                    RemoveTimer.scheduleAtFixedRate(new TimerTask() {
                        @Override
                        public void run() {
                            adapter.Remove();
                        }
                    }, 500, 500);
                } catch (SecurityException ex) {
                    Log.d("Scanner", ex.getMessage());
                }
            }
        } else {
            throw new Exception();
        }
    }

    public ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            try {
                if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
                    BluetoothDevice dev = result.getDevice();
                    String address = dev.getAddress();

                    for (BLEModel m : adapter.SynList) {
                        if (m.getAddress().equalsIgnoreCase(address)) {
                            m.updateDevice(result);
                            startOrScheduleSortingTask();
                            return;
                        }
                    }
                    BLEModel model = new BLEModel();
                    model.updateDevice(result);
                    if (Globals.isAccessCardAPPDevice(model.getType())) {
                        adapter.SynList.add(model);
                        Collections.sort(adapter.SynList, (model1, model2) -> {
                            double distance1 = model1.getDistance();
                            double distance2 = model2.getDistance();
                            return Double.compare(distance1, distance2);
                        });
                        adapter.notifyDataSetChanged();
                    } else
                        model = null;


                } else //if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST)
                {
                    Log.d("Scanner", String.valueOf(callbackType));
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    };

    private void startOrScheduleSortingTask() {
        if (!isSortingScheduled) {
            // 立即在主线程执行排序
            performSortingTask();
            // 每500毫秒重复执行排序任务
            mHandler.postDelayed(sortingRunnable, 500);
            isSortingScheduled = true;
        }
    }

    private Runnable sortingRunnable = new Runnable() {
        @Override
        public void run() {
            // 在主线程执行排序
            performSortingTask();
            // 500毫秒后调度下一次排序
            mHandler.postDelayed(this, 500);
        }
    };

    private void performSortingTask() {
        Collections.sort(adapter.SynList, (model1, model2) -> {
            double distance1 = model1.getDistance();
            double distance2 = model2.getDistance();
            return Double.compare(distance1, distance2);
        });

        adapter.notifyDataSetChanged();
    }

}
问题分析与解决思路

1. Android 13 BLE扫描的隐性限制

Android 13对BLE扫描新增了更严格的行为限制,即便权限齐全,也可能因为以下原因导致扫描失败:

  • 扫描过滤器匹配严格:代码中使用了ScanFilter过滤特定服务UUID,Android 13对UUID匹配的校验逻辑更苛刻,若广播包中的UUID格式(比如大小写、字节序)不符合要求,会直接被过滤掉。可以先移除过滤器测试,看是否能收到扫描结果。
  • 后台扫描资源限制:SCAN_MODE_LOW_LATENCY优先级虽高,但Android 13对后台扫描的系统资源调度限制更严。尝试在前台启动扫描,或者将扫描逻辑放入前台服务(添加前台通知),确保扫描任务处于活跃状态。

2. AltBeacon触发扫描恢复的原因

AltBeacon库内部的适配逻辑间接修复了扫描问题,可能的原因包括:

  • 系统BLE服务唤醒:AltBeacon初始化时会主动触发系统BLE扫描服务的初始化,而之前你的代码可能因某些原因未正确唤醒该服务,导致扫描回调无响应。
  • 兼容性扫描参数配置:AltBeacon使用的扫描设置(比如无严格过滤器、不同的回调类型组合)符合Android 13的调度要求,触发系统扫描服务正常工作后,你的扫描器也能共享这个状态。
  • 权限适配补充:AltBeacon内部针对Android 13做了额外的权限处理逻辑,间接解决了扫描的隐性权限问题。

3. 修复建议

  • 临时方案:如果不想依赖AltBeacon,可以参考其逻辑,在启动目标扫描前,先启动一次无过滤的临时扫描(持续1-2秒后停止),唤醒系统BLE服务后再启动你的扫描任务。
  • 排查过滤器问题:移除ScanFilter测试,若能收到结果,再检查目标UUID的格式是否完全匹配广播包中的UUID,或尝试使用模糊匹配方式。
  • 切换为前台扫描:将扫描逻辑放入前台服务,添加前台通知,避免系统因后台资源限制暂停扫描。
  • 确认蓝牙状态:启动扫描前,通过BluetoothAdapter.isEnabled()确认蓝牙已完全开启,必要时监听蓝牙状态广播,待蓝牙稳定后再启动扫描。

内容的提问来源于stack exchange,提问作者Spectra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 16:47:01