首页 > 解决方案 > 使用 bluetoothLEScanner().startScan 时未触发广播

问题描述

当设备进入具有特定 UUID 的信标区域时,我试图唤醒我的应用程序。

到目前为止,我尝试使用本机 BluetoothAdapter.getBluetoothLeScanner,提供 PendingIntent 来启动广播接收器。

我还尝试了 RxBleClient,这是一个很棒的库,在进行常规发现时确实有效。

我尝试了 AltBeacon库,它也是一个非常彻底和出色的库。

我还尝试了播放服务 Nearby Apis。

但都失败了。他们都可以定期扫描信标,但在尝试基本时,注册并让设备在 bg 中进行扫描。广播永远不会被触发。

我在使用前台服务时也尝试了所有 4 个。那也没有帮助。我还要提到这一切都是在 Android 8 上完成的。我检查了两个库中的代码,它们最终都使用来自本机蓝牙适配器的相同函数调用。所以我假设如果失败,其他一切也会失败。我还会提到,当为 startScan 函数提供 CallBack 而不是 PendingIntent 时,所有扫描的 BT 设备都会不断触发回调。如果我添加一个 ScanFilter 来过滤我的 UUID,它会失败。我错过了什么?

顺便说一句,当我在 Ios 中实现它时,它完美无缺。该应用程序被唤醒,就好像我使用了地理围栏进入/退出触发器一样。

为什么这在Android中不起作用?有什么想法吗?

编辑:这就是我使用 AltBeacon 库中的代码的方式。它基本上是 DOCS 教程的复制粘贴。唯一的区别是我将它封装在一个单独的类中:

public void init(AppCompatActivity context) {
    if (isRegistered) {
        return;
    }

    BluetoothManager bluetoothManager = (BluetoothManager) context.getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE);
    if (bluetoothManager != null) {
        bluetoothAdapter = bluetoothManager.getAdapter();
        if (!bluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            context.startActivityForResult(enableBtIntent, 1002);
        } else {
            register(context);
        }
    }
}

public void register(AppCompatActivity context) {

    if (isRegistered) {
        return;
    }

    executorService.execute(new Runnable() {
        @Override
        public void run() {
            Region region = new Region("com.sample.WakeOnDiscovery", Identifier.parse("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"), Identifier.fromInt(11180), null);
            bootstrapRegion = new RegionBootstrap(new BootstrapNotifier() {
                @Override
                public Context getApplicationContext() {
                    return Application.getContextFromApplicationClass();
                }

                @Override
                public void didEnterRegion(Region region) {
                    Logger.log(TAG, "didEnterRegion: " + region.toString());
                }

                @Override
                public void didExitRegion(Region region) {
                    Logger.log(TAG, "didExitRegion: " + region.toString());
                }

                @Override
                public void didDetermineStateForRegion(int i, Region region) {
                    Logger.log(TAG, "didDetermineStateForRegion: " + region.toString() + "|" + i);
                }
            }, region);
        }
    });

    isRegistered = true;
}

这是我尝试扫描这个特定的 UUID 和主要 int(并且没有主要 int)的时候。一旦我想,只有 didDetermineStateForRegion 被触发。int i = 0 或 1。

关于 UUID,我尝试了 2 个不同的 UUID。第一个是我在 IOS 中编写的示例应用程序中生成的。ios 应用程序将自己宣传为信标。我可以在使用常规发现功能的不同应用程序中看到它。但是 onEnterRegion 不会触发。

我还尝试了多年以来经常使用的 iBeacon 设备的 UUID。还是没有骰子。

当我尝试使用本机 android ble 扫描仪时,我使用这个:

ScanSettings settings = (new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)).setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES).build();
    List<ScanFilter> filters = new ArrayList<>(); // Make a scan filter matching the beacons I care about
    filters.add(new ScanFilter.Builder()/*.setServiceUuid(ParcelUuid.fromString("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"))*/.build());
    bluetoothAdapter.getBluetoothLeScanner().startScan(filters, settings, getPendingIntent(context));

和:

private PendingIntent getPendingIntent(Context context) {
    return PendingIntent.getBroadcast(context, 0, new Intent(context, BTDiscoveryBroadcast.class), PendingIntent.FLAG_UPDATE_CURRENT);
}

我尝试将 null 作为过滤器传递,我尝试了一个空数组列表,我尝试了一个具有相同 UUID 的实际过滤器。

仍然没有任何效果。

我在 logcat 中偶然看到的是这个错误消息:

E/NearbyDirect: Could not start scan. [CONTEXT service_id=49 ]
java.lang.IllegalStateException: Receiver com.google.location.nearby.direct.bluetooth.state.FastPairScanner$4@535d6ea registered with differing handler (was Handler (adxv) {8fab16a} now Handler (android.app.ActivityThread$H) {e4e0078})

我认为这在使用 AltBeacon 库时也会发生。我只能假设这与我的问题有关。

编辑2: 我试过这个:

ScanSettings settings = (new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)).setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES).build();
            List<ScanFilter> filters = new ArrayList<>(); // Make a scan filter matching the beacons I care about
            byte[] manufacturerData = new byte[] {
                    // iBeacon identifier prefix
                    0x02, 0x15,
                    // Your Proximity UUID
                    (byte) 0xE2, (byte) 0xC5, 0x6D, (byte) 0xB5, (byte) 0xDF, (byte) 0xFB, 0x48, (byte) 0xD2, (byte) 0xB0, 0x60, (byte) 0xD0, (byte) 0xF5, (byte) 0xA7, 0x10, (byte) 0x96, (byte) 0xE0,
                        0x3, 0x20, 0x3, 0x20, (byte) 0xC5
            };
            int manufacturerId = 0x004c; // Apple
            filters.add(new ScanFilter.Builder().setManufacturerData(manufacturerId, manufacturerData).build());
            bluetoothAdapter.getBluetoothLeScanner().startScan(filters, settings, getPendingIntent(context));

我在执行常规 .startDiscovery 扫描、定位我的 bt 设备并从 ScanResult 中提取数据后检索到的字节数据。只是为了确保它完全相同。

仍然没有触发广播:(

标签: androidbeaconaltbeacongoogle-beacon-platform

解决方案


如果您想设置扫描过滤器来查找信标 UUID,这样的代码将不起作用:

filters.add(new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")).build());

上面的代码将设置一个过滤器来寻找一个广播 GATT 服务 UUID 匹配 E2C56DB5-DFFB-48D2-B060-D0F5A71096E0 的 BLE 设备。GATT Service UUID 与 Bluetooth Beacon Proximity UUID 完全无关,即使两个 UUID 表面上看起来相同。换句话说,您是在告诉过滤器寻找可连接的蓝牙设备,该设备会宣传恰好与您的信标标识符相同的自定义服务。这将永远找不到任何东西,因为几乎可以保证这样的设备不存在。

相反,您想要做的事情更复杂 - 您想要设置一个扫描过滤器来查找蓝牙 LE 制造商广告(iBeacon 和 AltBeacon 广告是),它具有特定的制造商代码并以特定的字节序列开始.

获取过滤器的确切字节序列很棘手,因为它取决于制造商 ID 和信标布局(iBeacon 或 AltBeacon)。这是 Android 信标库为您处理的众多复杂性之一。

如果你真的需要自己做,你会为 iBeacon 做这样的事情(警告:未经测试的代码):

byte[] manufacturerData = new byte[] {
            // iBeacon identifier prefix
            0x02, 0x15, 
            // Your Proximity UUID
            0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0
            };
int manufacturerId = 0x004c; // Apple
ScanFilter filter =
filters.add(new ScanFilter.Buider().setManufacturerData(manufacturerId, manufacturerData).build());

推荐阅读