2013-11-19 2 views
48

Я пытаюсь написать приложение для отправки сообщений по Bluetooth Low Energy, которое затем будет передано UART в моем периферийном устройстве. Я выполнил шаги here, и приложение сканирует и находит устройство успешно. Однако соединение с использованием метода BluetoothGatt = BluetoothDevice.connectGatt (контекст, автосоединение, обратный вызов) завершается с ошибкой, при этом логарифм говорит «Не удалось зарегистрировать обратный вызов».Обратный вызов GATT не регистрируется

Вызов сделан из:

//device scan callback 
private BluetoothAdapter.LeScanCallback btScanCallback = new BluetoothAdapter.LeScanCallback() 
{ 
    @Override 
    public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) 
    {  
     some stuff 
     currBtGatt = device.connectGatt(parentActivity, false, btGattCallback); 
    } 
}; 

И в Гатт обратного вызова:

//GATT callback 
private BluetoothGattCallback btGattCallback = new BluetoothGattCallback() 
{  
    @Override 
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) 
    { 
     // if connected successfully 
     if(newState == BluetoothProfile.STATE_CONNECTED) 
     { 
      //discover services 
      updateStatus("Connected"); 
      gatt.discoverServices(); 
     } 
     else if(newState == BluetoothProfile.STATE_DISCONNECTED) 
     { 
      updateStatus("Disconnected"); 
     } 
    } 

    @Override 
    public void onServicesDiscovered(BluetoothGatt gatt, int status) 
    { 
     if(status == BluetoothGatt.GATT_SUCCESS) 
     { 
      //pick out the (app side) transmit channel 
      currBtService = gatt.getService(uartUuids[0]); 
      currBtCharacteristic = currBtService.getCharacteristic(uartUuids[1]); 
     } 
     else 
     { 
      updateStatus("Service discovery failed"); 
     } 
    } 
}; 

LogCat говорит:

11-19 10:40:39.363: D/BluetoothAdapter(11717): stopLeScan() 
11-19 10:40:39.373: D/BluetoothGatt(11717): connect() - device: DC:6D:75:0C:0F:F9, auto: false 
11-19 10:40:39.373: D/BluetoothGatt(11717): registerApp() 
11-19 10:40:39.373: D/BluetoothGatt(11717): registerApp() - UUID=3ba20989-5026-4715-add3-a5e31684009a 
11-19 10:40:39.373: I/BluetoothGatt(11717): Client registered, waiting for callback 
11-19 10:40:49.373: E/BluetoothGatt(11717): Failed to register callback 
11-19 10:40:49.533: D/BluetoothGatt(11717): onClientRegistered() - status=0 clientIf=5 
11-19 10:40:49.533: E/BluetoothGatt(11717): Bad connection state: 0 
11-19 10:40:49.593: D/BluetoothGatt(11717): onClientConnectionState() - status=0 clientIf=5 device=DC:6D:75:0C:0F:F9 
11-19 10:40:49.593: W/BluetoothGatt(11717): Unhandled exception: java.lang.NullPointerException 

Интересно, что мои периферические движется к "связ" состоянии (I имеют светодиоды индикации), и я могу подключиться к нему с одного телефона с демонстрационным приложением или с ПК BLE do ngle. Любые идеи оценили.

[EDIT] метод connectGatt возвращает значение null, которое, как я предполагаю, ожидается.

[EDIT] При проверке исходного кода API 18 появляется сообщение «Не удалось зарегистрировать обратный вызов», поскольку метод registerApp() возвращает false, потому что метод registerClient() IBluetoothGatt «mService» выбрасывает удаленное исключение, вероятно, на линии:

enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 

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

ответ

97

я, наконец, понял эту проблему вне. Устройство, которое я использую, - это Samsung Galaxy S4 и актуальная проблема (спасибо, что Wibble для руководства в вашем ответе, но вы немного в своем заключении), похоже, проблема с потоками.

В ответе Wibble он заявил, что добавление кнопки для подключения исправляет его проблему. Я начал задаваться вопросом, почему это имеет значение, и я также могу подключиться и отключиться в течение всего сеанса без кнопки GUI, используя потоки рабочего потока. Как только я закрою приложение, перезагрузите его и попытайтесь подключиться, я начинаю получать сообщение об ошибке «Ошибка регистрации обратного вызова». и ничего не работает. Я чуть не вытащил волосы из-за этого :)

См. Мои post в форумах Samsung для более подробной информации о моих точных проблемах.

Решение: Чтобы обойти эту проблему, просто убедитесь, что вы запускаете любой BLE код взаимодействия (устройство # connectGatt, подключение, отключение и т.д.) код в UIThread (с обработчиком, местные службы или активность # runOnUiThread). Следуйте этому правилу, и вы, надеюсь, избежите этой ужасной проблемы.

Глубоко в нашей библиотеке, я только имел доступ к контексту приложения. Вы можете создать обработчик из контекста, который будет помещаться в основной поток, используя new Handler(ctx.getMainLooper());

Если вы столкнулись с другими проблемами подключения, разверните пример приложения в samples\android-18\legacy\BluetoothLeGatt и посмотрите, работает ли это приложение. Это было своего рода базой для понимания того, что BLE действительно работает с моим периферийным устройством, и дал мне надежду, что, если я выкопаю достаточно в нашей библиотеке, я в конце концов найду ответ.

EDIT: Я не видел эту проблему «Не удалось зарегистрировать обратный вызов» на Nexus 4, Nexus 5 или Nexus 7 2013 при использовании фоновых потоков для выполнения операций BLE. Это может быть проблемой в реализации Samsungs 4.3.

+2

Большое вам спасибо! Я просто не мог понять, почему мое приложение регистрируется без каких-либо исключений, когда я вызываю connectGatt, но почему не был вызван GattCallback. Вызов connectGatt на основной поток исправил это! –

+0

@ Linard Рад помочь. Я думаю, что это ошибка, и ее нужно решить. До тех пор, спасибо, Stack Overflow –

+3

Это такие неприятные ошибки ... Google может просто упомянуть что-то вроде «нужно вызывать из основного потока» в документах –

-2

Для того, чтобы автоматически подключаться к устройству Bluetooth, т.е. без явного ввода пользователем, как я пытался это сделать, требуется разрешение BLUETOOTH_PRIVILEDGE. Однако это недоступно для сторонних приложений, поэтому мой код не удался. Добавление опции меню для подключения и использования одного и того же кода отлично работает.

http://developer.android.com/reference/android/Manifest.permission.html#BLUETOOTH_PRIVILEGED

+0

У меня никогда не было такого же опыта. Мое приложение сканирует и подключает периферийные устройства без ввода пользователя и работает правильно. В документе говорится, что BLUETOOTH_PRIVILEGED требуется для «подключения Bluetooth-устройств без взаимодействия с пользователем», что совсем не так, как «подключиться». Я думаю, что используемое периферийное устройство требует сопряжения. – reTs

+0

@reTs Я не согласен, потому что a) функция 'connectGatt()' не пытается скомпоновать (похоже, что только уже подключенные устройства могут быть спарены) и b) насколько я знаю, в исходном коде нет ничего для периферийного запрошенного сопряжения/аутентификации. Но, спасибо за указание, что есть разница, BLUETOOTH_PRIVILEDGED вполне может быть неактуальным. – Wibble

3

Итак, моя проблема заключалась в использовании рекурсивной службы. connectGatt отлично работал с lollipop, но более старые версии возвратили null. работает на основной теме, решил проблему. Это мое решение:

public void connectToDevice(String deviceAddress) { 
    mDeviceAddress = deviceAddress; 
    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mDeviceAddress); 

    Handler handler = new Handler(Looper.getMainLooper()); 
    handler.post(new Runnable() { 
     @Override 
     public void run() { 


      if (device != null) { 

       mGatt = device.connectGatt(getApplicationContext(), true, mGattCallback); 
       scanLeDevice(false);// will stop after first device detection 
      } 
     } 
    }); 
} 
1

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

Вот список вещей, чтобы сделать:.

  1. Maker, что вы используете новый обработчик (Looper.getMainLooper()) пост (новый Runnable) любой операции Gatt (подключение, отключение, закрыть), но также и на операциях сканера (startScan, stopScan и т. д.).

  2. Существует состояние гонки для прямого подключения на Android 6 (или, может быть, 5), поэтому попробуйте подключить GATT так:

    new Handler(getContext().get().getMainLooper()).post(() -> { 
        if (CommonHelper.isNOrAbove()) { 
         connectedGatt = connectedBLEDevice.connectGatt(context.get(), true, gattCallback, BluetoothDevice.TRANSPORT_AUTO); 
         Timber.tag("HED-BT").d("Connecting BLE after N"); 
        } else { 
         try { 
          Method connectGattMethod = connectedBLEDevice.getClass().getMethod("connectGatt", Context.class, boolean.class, BluetoothGattCallback.class, int.class); 
          connectedGatt = (BluetoothGatt) connectGattMethod.invoke(connectedBLEDevice, context.get(), false, gattCallback, BluetoothDevice.TRANSPORT_AUTO); 
          Timber.tag("HED-BT").d("Connecting BLE before N"); 
         } catch (Exception e) { 
          failedConnectingBLE(); 
         } 
        } 
    }); 
    
  3. При отключении GATT, вызовите разъединение() первый и закрыть() после в рутине GattCallback.

Смежные вопросы