android - principios - La devolución de llamada del GATT no se registra
principios de la omc pdf (4)
Intento escribir una aplicación para enviar mensajes a través de Bluetooth Low Energy, que luego pasará a UART en mi periférico. He seguido los pasos here y la aplicación busca y encuentra el dispositivo con éxito. Sin embargo, la conexión que utiliza el método BluetoothGatt = BluetoothDevice.connectGatt (contexto, conexión automática, devolución de llamada) falla, y el logcat dice "Error al registrar la devolución de llamada".
Llamada hecha desde:
//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);
}
};
Y la devolución de llamada de Gatt:
//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 dice:
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
Curiosamente, mi periférico se mueve a un estado "conectado" (tengo indicadores LED) y puedo conectarme a él desde el mismo teléfono con una aplicación de demostración o con un dongle PC BLE. Cualquier idea apreciada.
[EDIT] el método connectGatt devuelve null, que supongo que se espera.
[EDITAR] Al inspeccionar el código fuente API 18, parece que el mensaje "No se pudo registrar la devolución de llamada" se entrega porque el método registerApp () devuelve falso porque el método registerClient () del IBluetoothGatt "mService" arroja una excepción remota, probablemente en la línea:
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
porque el mensaje de registro en la siguiente línea nunca se ve. Entonces podría ser una cuestión de permisos, excepto que la aplicación tiene permisos bluetooth y blue_admin.
Entonces, mi problema fue ejecutarlo desde un servicio recursivo. connectGatt funcionó bien con lollipop, pero las versiones anteriores arrojaron null. corriendo en un hilo principal resolvió el problema. Esta es mi solución:
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
}
}
});
}
Finalmente me di cuenta de este problema. El dispositivo que estoy usando es un Samsung Galaxy S4 y el problema real (gracias a Wibble como guía en tu respuesta, pero estás ligeramente apagado en tu conclusión) parece ser un problema de enhebrado.
En la respuesta de Wibble, afirmó que agregar un botón para conectar solucionó su problema. Comencé a preguntarme por qué eso es importante, y también puedo conectarme y desconectarme bien durante una sesión completa sin un botón de GUI usando hilos de trabajo en segundo plano. Tan pronto como fuerzo a cerrar mi aplicación, la reinicio y trato de conectarme, empiezo a recibir el error "No se pudo registrar la devolución de llamada". y nada funciona más. Casi me saqué el pelo por esto :)
Ver mi post en los foros de Samsung para más detalles sobre mis problemas exactos.
Solución: para evitar este problema, solo asegúrese de ejecutar cualquier código de interacción BLE (device # connectGatt, connect, disconnect, etc) en UIThread (con un controlador, servicio local o Activity # runOnUiThread). Siga esta regla práctica y con suerte evitará este terrible problema.
En lo profundo de nuestra biblioteca, solo tuve acceso al contexto de la aplicación. Puede crear un controlador a partir de un contexto que se publicará en el hilo principal utilizando el new Handler(ctx.getMainLooper());
Si enfrenta otros problemas de conexión, despliegue la aplicación de muestra en samples/android-18/legacy/BluetoothLeGatt
y vea si esa aplicación funciona. Esa fue una especie de punto de partida para darme cuenta de que BLE realmente funciona con mi periférico, y me dio esperanza de que si cavaba lo suficiente en nuestra biblioteca eventualmente encontraría la respuesta.
EDITAR: No vi este problema de ''No se pudo registrar la devolución de llamada'' en el Nexus 4, Nexus 5 o Nexus 7 2013 cuando se utilizan subprocesos en segundo plano para realizar operaciones BLE. Puede ser solo un problema en la implementación de Samsung 4.3.
Para poder conectarme automáticamente a un dispositivo Bluetooth, es decir, sin la intervención explícita del usuario como estaba intentando hacer, se requiere el permiso BLUETOOTH_PRIVILEDGE. Sin embargo, esto no está disponible para aplicaciones de terceros, por lo que mi código falló. Agregar una opción de menú para conectarse y usar el mismo código funciona bien.
http://developer.android.com/reference/android/Manifest.permission.html#BLUETOOTH_PRIVILEGED
También puedo confirmar que Lo-Tan es la respuesta para verificar primero. He probado muchos dispositivos, algunos de ellos se comportan bien cuando se ejecuta desde un hilo secundario. Algunos pueden bloquear después de un tiempo, el comportamiento es imprevisto.
Aquí está la lista de cosas que hacer:
Maker asegúrese de utilizar el nuevo controlador (Looper.getMainLooper ()). Post (nuevo ejecutable) en cualquier operación de gatt (conectar, desconectar, cerrar), sino también en las operaciones del escáner (startScan, stopScan, etc.).
Hay una condición de carrera para la conexión directa en Android 6 (o tal vez 5) así que intente conectar Gatt de esta manera:
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(); } } });
Al desconectar el gatt, llame a disconnect () primero y close () después en la rutina GattCallback.