Android GATT回调注册失败
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20069507/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
GATT callback fails to register
提问by Wibble
I'm trying to write an application to send messages over Bluetooth Low Energy, which will then be passed on by UART in my peripheral. I've followed the steps hereand the app scans for and finds the device successfully. However, connection using the BluetoothGatt = BluetoothDevice.connectGatt(context, autoconnect, callback) method fails, with logcat saying "Failed to register callback".
我正在尝试编写一个应用程序来通过低功耗蓝牙发送消息,然后这些消息将通过我的外围设备中的 UART 传递。我已按照此处的步骤操作,应用程序会成功扫描并找到设备。但是,使用 BluetoothGatt = BluetoothDevice.connectGatt(context, autoconnect, callback) 方法的连接失败,logcat 显示“无法注册回调”。
Call made from:
呼叫来自:
//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);
}
};
And the Gatt callback:
和 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 says:
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
Interestingly, my peripheral moves to a "connected" state (I have indication LEDs) and I can connect to it from the same phone with a demonstration application or with a PC BLE dongle. Any ideas appreciated.
有趣的是,我的外围设备移动到“连接”状态(我有 LED 指示灯),我可以通过演示应用程序或 PC BLE 加密狗从同一部手机连接到它。任何想法表示赞赏。
[EDIT] the connectGatt method returns null, which I guess is expected.
[编辑] connectGatt 方法返回 null,我猜这是预期的。
[EDIT] On inspection of API 18 source code, it appears that the "Failed to register callback" message is delivered because the method registerApp() returns false because the registerClient() method of the IBluetoothGatt "mService" throws a remote exception, probably at the line:
[编辑] 在检查 API 18 源代码时,似乎传递了“无法注册回调”消息,因为方法 registerApp() 返回 false,因为 IBluetoothGatt“mService”的 registerClient() 方法抛出远程异常,可能在线:
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
because the log message in the very next line is never seen. So it could be a permissions thing, except that the application hasbluetooth and bluetooth_admin permissions.
因为下一行中的日志消息从未出现过。所以它可能是一个权限问题,除了应用程序具有bluetooth 和 bluetooth_admin 权限。
回答by Lo-Tan
I finally figured this problem out. The device I am using is a Samsung Galaxy S4 and the actual problem (thanks Wibble for guidance in your answer, but you are slightly off in your conclusion) appears to be a threading issue.
我终于想通了这个问题。我使用的设备是三星 Galaxy S4,实际问题(感谢 Wibble 在您的回答中提供指导,但您的结论略有偏差)似乎是线程问题。
In Wibble's answer, he stated that adding a button to connect fixed his issue. I started wondering why that matters, and I also can connect and disconnect fine during an entire session without a GUI button using background worker threads. As soon as I force close my application, restart it, and try to connect, I start getting the error "Failed to register callback." and nothing works any more. I almost pulled my hair out over this one :)
在 Wibble 的回答中,他表示添加一个按钮来连接解决了他的问题。我开始想知道为什么这很重要,而且我还可以在整个会话期间使用后台工作线程在没有 GUI 按钮的情况下正常连接和断开连接。一旦我强制关闭我的应用程序,重新启动它并尝试连接,我就会开始收到错误“无法注册回调”。没有任何作用了。我差点把头发拉到这个上面:)
See my postin Samsung's forums for more detail on my exact issues.
有关我的确切问题的更多详细信息,请参阅我在三星论坛中的帖子。
Solution:To get around this issue, just make sure you run any BLE interaction code (device#connectGatt, connect, disconnect, etc) code in the UIThread (with a handler, local service, or Activity#runOnUiThread). Follow this rule of thumb and you will hopefully avoid this dreadful problem.
解决方案:要解决此问题,只需确保在 UIThread 中运行任何 BLE 交互代码(device#connectGatt、connect、disconnect 等)(使用处理程序、本地服务或 Activity#runOnUiThread)。遵循这条经验法则,您将有望避免这个可怕的问题。
Deep in our library, I only had access to the application context. You can create a handler from a context that will post to the main thread by using new Handler(ctx.getMainLooper());
在我们的图书馆深处,我只能访问应用程序上下文。您可以从上下文创建一个处理程序,该处理程序将通过使用new Handler(ctx.getMainLooper());
If you face other connection problems, deploy the sample app in samples\android-18\legacy\BluetoothLeGatt
and see if that application works. That was kind of my baseline for realizing BLE does actually work with my peripheral, and gave me hope that if I dug enough in our library I would eventually find the answer.
如果您遇到其他连接问题,samples\android-18\legacy\BluetoothLeGatt
请在其中部署示例应用程序并查看该应用程序是否有效。这是我意识到 BLE 确实与我的外围设备一起工作的基准,并给了我希望,如果我在我们的库中挖掘得足够多,我最终会找到答案。
EDIT:I did not see this 'Failed to register callback' issue on the Nexus 4, Nexus 5, or Nexus 7 2013 when using background threads to perform BLE operations. It may just be an issue in Samsungs 4.3 implementation.
编辑:当使用后台线程执行 BLE 操作时,我没有在 Nexus 4、Nexus 5 或 Nexus 7 2013 上看到这个“无法注册回调”问题。这可能只是三星 4.3 实施中的一个问题。
回答by TacoEater
So, My problem was running it from a recursive service. connectGatt worked fine with lollipop but older versions returned null. running on a main thread solved the problem. This is my solution:
所以,我的问题是从递归服务运行它。connectGatt 与棒棒糖一起工作正常,但旧版本返回 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
}
}
});
}
回答by HED Tech
I can also confirm that Lo-Tan is the answer to check first. I've tested a lot of devices, some of them are behaving well when you run from a secondary thread. Some may block after a while, the behaviour is unpredicted.
我也可以确认 Lo-Tan 是首先检查的答案。我已经测试了很多设备,当您从辅助线程运行时,其中一些设备运行良好。有些可能会在一段时间后阻塞,行为是不可预测的。
Here is the list of things to do:
以下是要做的事情列表:
Maker sure you use new Handler(Looper.getMainLooper()).post(new Runnable) on any gatt operation(connect, disconnect, close) but also on the scanner operations(startScan, stopScan etc.).
There is race condition for direct connection on Android 6 (or maybe 5) so try to connect gatt like this:
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(); } } });
When disconnecting the gatt, call disconnect() first and close() after in the GattCallback routine.
确保在任何 gatt 操作(连接、断开连接、关闭)以及扫描器操作(startScan、stopScan 等)上使用 new Handler(Looper.getMainLooper()).post(new Runnable )。
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(); } } });
断开 gatt 时,在 GattCallback 例程中先调用 disconnect(),然后调用 close()。
回答by Wibble
In order to automatically connect to a Bluetooth device, ie without explicit user input as I was attempting to do, the permission BLUETOOTH_PRIVILEDGE is required. However, this is not available to third-party applications so my code failed. Adding a menu option to connect and using the same code works fine.
为了自动连接到蓝牙设备,即没有我试图做的明确的用户输入,需要许可 BLUETOOTH_PRIVILEDGE。但是,这不适用于第三方应用程序,因此我的代码失败了。添加一个菜单选项来连接并使用相同的代码工作正常。
http://developer.android.com/reference/android/Manifest.permission.html#BLUETOOTH_PRIVILEGED
http://developer.android.com/reference/android/Manifest.permission.html#BLUETOOTH_PRIVILEGED