Изменение рекламы BLE в Android

Я новичок в Android и BLE. В настоящее время я пытаюсь рекламировать пакет данных, который периодически изменяется в Android через BLE. Я использовал следующий код, который был доступен в https://source.android.com/devices/bluetooth/ble_advertising.

BluetoothLeAdvertiser advertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
        AdvertisingSetParameters parameters = (new AdvertisingSetParameters.Builder())
                .setLegacyMode(true) // True by default, but set here as a reminder.
                .setConnectable(false)
                .setInterval(AdvertisingSetParameters.INTERVAL_HIGH)
                .setTxPowerLevel(AdvertisingSetParameters.TX_POWER_HIGH)
                .build();
        AdvertiseData data = (new AdvertiseData.Builder()).setIncludeDeviceName(true).build();

        final AdvertisingSet[] currentAdvertisingSet = new AdvertisingSet[1];
        //final AdvertisingSet[] currentAdvertisingSet = {null};
        AdvertisingSetCallback callback = new AdvertisingSetCallback() {
            @Override
            public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) {
                Log.i(LOG_TAG, "onAdvertisingSetStarted(): txPower:" + txPower + " , status: "
                        + status);
                currentAdvertisingSet[0] = advertisingSet;
            }

            @Override
            public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {
                Log.i(LOG_TAG, "onAdvertisingDataSet() :status:" + status);
            }

            @Override
            public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {
                Log.i(LOG_TAG, "onScanResponseDataSet(): status:" + status);
            }

            @Override
            public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {
                Log.i(LOG_TAG, "onAdvertisingSetStopped():");
            }
        };

        //start advertising
        advertiser.startAdvertisingSet(parameters, data, null, null, null, callback);

        //change the advertising packet
        currentAdvertisingSet[0].setAdvertisingData(new AdvertiseData.Builder().setIncludeDeviceName(true).setIncludeTxPowerLevel(true).build());

Но когда я пытаюсь назначить новые рекламные данные последней строкой, я получаю

Attempt to invoke virtual method 'void android.bluetooth.le.AdvertisingSet.setAdvertisingData(android.bluetooth.le.AdvertiseData)' on a null object reference

ошибка и приложение закрыто с обоими значениями setLegacyMode true и false. Но я уже назначил AdvertisingSet в функции public void onAdvertisingSetStarted. Что мне здесь нужно делать?


person Lahiru Gunathilake    schedule 03.05.2020    source источник


Ответы (1)


Причина, по которой показанный код вызывает исключение NullPointerException, заключается в том, что он пытается получить доступ к currentAdvertisingSet[0] до того, как вы присвоили значение этому элементу массива.

Когда код инициализирует его с final AdvertisingSet[] currentAdvertisingSet = new AdvertisingSet[1];, тогда содержимое массива инициализируется с каждым элементом, установленным в null. Код не инициализирует currentAdvertisingSet[0] ненулевым значением до выполнения AdvertisingSetCallback. Это асинхронно и произойдет через некоторое время после вызова advertiser.startAdvertisingSet(...).

Проблема в том, что этот обратный вызов еще не произошел, когда следующая строка currentAdvertisingSet[0].setAdvertisingData(...) выполняется через несколько микросекунд. Когда он выполняется, элемент currentAdvertisingSet[0] еще не инициализирован - он по-прежнему нулевой. Вот почему код вылетает.

Чтобы исправить это, вы должны дождаться использования currentAdvertisingSet[0], пока он не будет инициализирован. Вы, конечно, можете добавить проверку типа if (currentAdvertisingSet[0] != null), чтобы предотвратить сбой, но в показанном коде это никогда не будет правдой, поэтому код никогда не будет выполнен.

В конечном итоге вам нужно будет переместить код, который изменяет рекламный набор, так, чтобы он выполнялся позже. Вы можете поместить этот код в обратный вызов, но это может не иметь смысла для вашего варианта использования - вероятно, нет смысла запускать рекламу, а затем сразу же менять ее на что-то другое.

person davidgyoung    schedule 03.05.2020
comment
Большое спасибо за Ваш ответ. Даже если функция onAdvertisingSetStarted() вызывается, currentAdvertisingSet[0] не будет назначена. Я очень признателен, если вы дадите какие-то инструкции по решению проблемы. Спасибо еще раз. - person Lahiru Gunathilake; 03.05.2020
comment
Я не думаю, что это правда. Я предлагаю вам установить точку останова в этой строке currentAdvertisingSet[0] = advertisingSet; или добавить строку отладки, которая выводит ее значение. Я подозреваю, что вы обнаружите, что это имеет значение. - person davidgyoung; 03.05.2020
comment
Проблема возникла из-за того, что в моей конфигурации был выбран неверный режим прежних версий. Мне удалось поместить currentAdvertisingSet[0].setAdvertisingData(new AdvertiseData.Builder().setIncludeDeviceName(true).setIncludeTxPowerLevel(true).build()); в отдельный поток и успешно изменить данные рекламы во время выполнения. Ваши инструкции и советы действительно помогли мне в решении моей задачи. Я очень благодарен за вашу помощь. - person Lahiru Gunathilake; 06.05.2020