Bluetooth Low Energyを介してメッセージを送信するアプリケーションを作成しようとしています。このメッセージは、周辺機器でUARTによって渡されます。手順に従いました here =アプリはデバイスをスキャンして正常に検出しますが、BluetoothGatt = BluetoothDevice.connectGatt(context、autoconnect、callback)メソッドを使用した接続は失敗し、logcatは「コールバックの登録に失敗しました」と表示します。
発信元:
//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コールバック:
//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
興味深いことに、私の周辺機器は「接続済み」状態に移行し(表示LEDがあります)、デモ用アプリケーションまたはPC BLEドングルを使用して同じ電話から接続できます。どんなアイデアも感謝します。
[編集] connectGattメソッドはnullを返しますが、これは予想されることです。
[編集] API 18ソースコードを検査すると、IBluetoothGatt "mService"のregisterClient()メソッドがリモート例外をスローするためにregisterApp()メソッドがfalseを返すため、「コールバックの登録に失敗しました」メッセージが配信されます行で:
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
これは、次の行のログメッセージが表示されないためです。そのため、アプリケーションhasbluetoothおよびbluetooth_admin許可を除いて、許可の問題になる可能性があります。
私はついにこの問題を見つけました。私が使用しているデバイスはSamsung Galaxy S4であり、実際の問題(回答のガイダンスについてはWibbleに感謝しますが、結論は少しずれています)はスレッド化の問題のようです。
Wibbleの答えでは、接続するボタンを追加することで問題が解決したと述べました。それがなぜ重要なのか疑問に思い始め、バックグラウンドワーカースレッドを使用するGUIボタンなしで、セッション全体で正常に接続および切断することもできます。アプリケーションを強制的に閉じて再起動し、接続を試みるとすぐに、「コールバックの登録に失敗しました」というエラーが表示され始めます。もう何も機能しません。私はこれの上で髪を引っ張っていました:)
正確な問題の詳細については、Samsungのフォーラムで post を参照してください。
解決策:この問題を回避するには、UIThreadでBLE相互作用コード(device#connectGatt、connect、disconnectなど)のコードを実行してください。 (ハンドラー、ローカルサービス、またはActivity#runOnUiThreadを使用)。この経験則に従うと、この恐ろしい問題を避けることができます。
ライブラリの奥深くでは、アプリケーションコンテキストにしかアクセスできませんでした。 new Handler(ctx.getMainLooper());
を使用して、メインスレッドに投稿するコンテキストからハンドラーを作成できます。
他の接続の問題に直面している場合は、samples\Android-18\legacy\BluetoothLeGatt
そして、そのアプリケーションが機能するかどうかを確認します。それはBLEが実際に私の周辺機器で動作することを理解するための私のベースラインのようなものであり、ライブラリを十分掘り下げれば最終的に答えを見つけることを希望しました。
EDIT:バックグラウンドスレッドを使用して実行するときに、Nexus 4、Nexus 5、またはNexus 7 2013でこの「コールバックの登録に失敗しました」問題が表示されませんでしたBLE操作。これは、Samsungs 4.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
}
}
});
}
Lo-Tanが最初に確認する答えであることも確認できます。私は多くのデバイスをテストしましたが、セカンダリスレッドから実行した場合、一部のデバイスは正常に動作します。しばらくするとブロックされる場合がありますが、動作は予測できません。
行うべきことのリストはここにあります:
メーカーは必ず、すべてのgatt操作で新しいHandler(Looper.getMainLooper())。post(new Runnable)を使用してください(接続、切断、閉じる) スキャナー操作(startScan、stopScanなど)。
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()を呼び出します。