我正在尝试连接到Android上的蓝牙设备.我在onClientConnectionState处理程序中收到状态133.我并不总是得到这个错误 – 有时连接很好.我无法指出触发问题的原因.重新启动设备和我的repro应用程序后,我甚至立即使用它.

我知道几个问题并建议解决这个问题,包括(从here,here和here):

>将UI线程用于所有BT API

>完成后务必关闭关贸总协定

但我正在做这一切.更重要的是,我的设备是一个Nexus 5(运行Lollipop),根据一些人甚至不需要BT交互在UI线程上.

我把最简单的复制品放在了一起.它在C#中,但Java等价物应该是显而易见的:

[Activity(Label = "BluetoothGatt133ErrorRepro", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
protected override void OnCreate(Android.OS.Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
var button = FindViewById(Resource.Id.button);
button.Click += this.OnClick;
}
private async void OnClick(object sender, EventArgs e)
{
Action log = message => Console.WriteLine($"***** #{Environment.CurrentManagedThreadId} {message}");
log("Beginning");
var bluetoothManager = (BluetoothManager)Application.Context.GetSystemService(Context.BluetoothService);
var adapter = bluetoothManager.Adapter;
var scanner = adapter.BluetoothLeScanner;
var callback = new Callback();
var filters = new List();
var settings = new ScanSettings.Builder()
.SetScanMode(global::Android.Bluetooth.LE.ScanMode.LowLatency)
.Build();
log("Starting scan");
scanner.StartScan(filters, settings, callback);
var result = await callback.Result;
log($"Got device: {result.Device.Name}");
var remoteDevice = adapter.GetRemoteDevice(result.Device.Address);
var gattCallback = new GattCallback(log);
log("Connecting GATT");
var gatt = remoteDevice.ConnectGatt(Application.Context, true, gattCallback);
gatt.Connect();
await gattCallback.Result;
log("Disconnecting GATT");
gatt.Close();
gatt.Dispose();
}
private sealed class Callback : ScanCallback
{
private readonly TaskCompletionSource result;
public Callback()
{
this.result = new TaskCompletionSource();
}
public Task Result => this.result.Task;
public override void OnBatchScanResults(IList results)
{
foreach (var result in results)
{
this.HandleResult(result);
}
}
public override void OnScanResult(ScanCallbackType callbackType, ScanResult result)
{
this.HandleResult(result);
}
public override void OnScanFailed(ScanFailure errorCode)
{
this.result.TrySetException(new InvalidOperationException($"Failed with error code {errorCode}."));
}
private void HandleResult(ScanResult result)
{
if (result.Device.Name.Contains("elided"))
{
this.result.TrySetResult(result);
}
}
}
private sealed class GattCallback : BluetoothGattCallback
{
private readonly Action log;
private readonly TaskCompletionSource result;
public GattCallback(Action log)
{
this.log = log;
this.result = new TaskCompletionSource();
}
public Task Result => this.result.Task;
public override void OnConnectionStateChange(BluetoothGatt gatt, GattStatus status, ProfileState newState)
{
this.log($"Connection state changed to {newState} with status {status}.");
this.result.TrySetResult(true);
}
}
}

这是运行它的输出(我已经离开了Android的BluetoothGatt源的输出):

***** #1 Beginning
***** #1 Starting scan
07-01 11:53:21.458 D/BluetoothLeScanner(10377): onClientRegistered() - status=0 clientIf=5
***** #1 Got device: elided
***** #1 Connecting GATT
07-01 11:53:22.833 D/BluetoothGatt(10377): connect() - device: 00:00:DE:AD:BE:EF, auto: true
07-01 11:53:22.833 D/BluetoothGatt(10377): registerApp()
07-01 11:53:22.833 D/BluetoothGatt(10377): registerApp() - UUID=fa5bce8a-416d-47fe-9a8a-e44156f7e865
07-01 11:53:22.834 D/BluetoothGatt(10377): onClientRegistered() - status=0 clientIf=6
07-01 11:53:24.622 D/BluetoothGatt(10377): onClientConnectionState() - status=133 clientIf=6 device=00:00:DE:AD:BE:EF
***** #4 Connection state changed to Disconnected with status 133.
***** #1 Disconnecting GATT
07-01 11:53:24.707 D/BluetoothGatt(10377): close()
07-01 11:53:24.707 D/BluetoothGatt(10377): unregisterApp() - mClientIf=6

如您所见,我与蓝牙堆栈的所有交互都发生在主线程上(#1).但尽管如此,我在onClientConnectionState处理程序中收到状态133.

我的清单有这些权限:

我正在使用最新的Marshmallow工具进行编译,目标是Marshmallow,最低目标为4.0.3(API级别15).

可能是什么导致了这个?

解决方法:

(注意:你可能已经这样做了,但我不熟悉C#)

根据我的经验,实际上并不是你只是在主线程上与BLE设备进行交互,只是你不会立即向设备发出太多请求.

我曾经在Android上使用BLE时遇到这个问题(并阅读关于使用主线程的类似评论),这是因为我向远程Gatt发布了太多请求(读/写,通知/指示注册等)在接收BluetoothGattCallback对象中先前操作的回调之前的设备.我设置了自己的托管gatt操作队列(一个阻塞的线程,直到在GattCallback中收到该操作的回调,或者初始读/写操作返回false,然后处理下一个排队操作或使用退避乘数重试),因为我没有遇到过这个问题.据我所知,Android并没有很好地完成“排队”操作,因此“isBusy”布尔会在不知情的情况下咬你(如果没有跳出来,请查看BlueoothGattCharacteristic写入方法).我还注意到你不想在你的回调对象中做很多工作,但是将回调委托给另一个线程或者广播结果(所以你不要阻止Binder线程).通常我只是复制字节有效负载,并将其传递给另一个HandlerThread进行解析.

此外,是的,断断续续和关闭是非常重要的.我通常使用服务来处理BLE交互,并在服务的onDestroy完成之前调用这两者.

标签:c,java,android,bluetooth,bluetooth-lowenergy