android,Android,Bluetooth Lowenergy,android" /> android,Android,Bluetooth Lowenergy,android" />

Android 4.3:如何连接多个蓝牙低能耗设备

Android 4.3:如何连接多个蓝牙低能耗设备,android,bluetooth-lowenergy,android,Android,Bluetooth Lowenergy,android,我的问题是:安卓4.3(客户端)能否与多个BLE设备(服务器)建立活动连接?如果是,我如何实现它? 到目前为止我做了什么 我尝试评估使用BLE和Android 4.3 BLE API可以实现的吞吐量。此外,我还试图找出有多少设备可以同时连接和激活。我使用Nexus7(2013),安卓4.4作为主控,TI CC2540 Keyfob作为从控 我为从机编写了一个简单的服务器软件,它通过BLE通知传输10000个20字节的数据包。我的Android应用程序基于Bluetooth SIG的 它适用于一台

我的问题是:安卓4.3(客户端)能否与多个BLE设备(服务器)建立活动连接?如果是,我如何实现它?

到目前为止我做了什么

我尝试评估使用BLE和Android 4.3 BLE API可以实现的吞吐量。此外,我还试图找出有多少设备可以同时连接和激活。我使用Nexus7(2013),安卓4.4作为主控,TI CC2540 Keyfob作为从控

我为从机编写了一个简单的服务器软件,它通过BLE通知传输10000个20字节的数据包。我的Android应用程序基于Bluetooth SIG的

它适用于一台设备,我可以以7.5毫秒的连接间隔实现约56 kbit的有效负载吞吐量。要连接到多个从属设备,我遵循了一位北欧员工的建议,他在报告中写道:

是的,一个应用程序可以处理多个从机。您需要使用一个BluetoothGatt实例来处理每个从机。您还需要为连接到的每个从机指定BluetoothGattCallback

所以我试过了,它部分有效。我可以连接到多个从属服务器。我还可以注册多个从属服务器上的通知。当我开始测试时,问题就开始了。我首先收到来自所有从属设备的通知,但经过几次连接间隔后,只有来自一个设备的通知通过。大约10秒钟后,其他从属设备断开连接,因为它们似乎达到连接超时。有时,我从测试一开始就只收到一个从机的通知

我还尝试通过读取操作访问属性,结果相同。读了几遍之后,只有一台设备的答案出来了

我知道在这个论坛上有一些类似的问题:,或者。但这些答案都没有让我明白,如果可能的话,以及如何做到这一点


非常感谢您的建议。

不幸的是,当前Android BLE堆栈中的通知有点缺陷。有一些硬编码的限制,我发现一些稳定性问题,即使是一个单一的设备。(我曾经读到,您只能有4个通知……不确定是跨所有设备还是每个设备发送。现在正在尝试查找该信息的来源。)

我会尝试切换到一个轮询循环(比如,每秒钟轮询一次问题项),看看你是否发现自己的稳定性有所提高。我也会考虑切换到一个不同的从设备(比如说HRM或TI SeSONTAG标签),看看是否有一个与从属侧代码有关的问题(除非您可以测试IOS或其他平台的情况,并确认它不是问题的一部分)。
编辑

我自己正在开发一款具有BLE功能的应用程序。我设法连接到多个设备并打开通知的方法是实现延迟

因此,我创建了一个新线程(为了不阻塞UI线程),并在新线程中连接并打开通知

例如,在BluetoothDevice.connectGatt()之后;调用Thread.sleep()

并为读/写和启用/禁用通知添加相同的延迟

编辑

使用这样的等待,这样Android dindn就不会重新启用ANR

public static boolean waitIdle() {
        int i = 300;
        i /= 10;
        while (--i > 0) {
            if (true)
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

        }

        return i > 0;
    }

Rain的回答是正确的,当你在Android中使用BLE时,几乎所有事情都需要延迟。我用它开发了几个应用程序,这是非常必要的。通过使用它们,您可以避免大量的崩溃

在我的例子中,我在每个读/写命令之后使用延迟。这样做,您可以确保几乎总是收到来自BLE设备的响应。我这样做:(当然,所有的事情都是在一个单独的线程中完成的,以避免在主线程上做太多的工作)

或:

当您读/写一行中的几个特征时,这非常有用。。。由于Android的速度足够快,几乎可以立即执行命令,如果不在命令之间使用延迟,可能会出现错误或不一致的值

希望它能帮助你,即使它不是你问题的答案。

重新审视问题:我仍然在使用延迟

概念:在每一个引发问题的重大行动(例如,会议、服务发现、写入、读取)之后,都需要一个协议。另外,请看一看BLE上的谷歌示例,了解广播应该如何发送,并获得一些一般性的理解等等

首先,(或)对于Bluetooth设备,使用所需设备填充连接队列,并调用initConnection()

看看下面的例子

private Queue<BluetoothDevice> connectionQueue = new LinkedList<BluetoothDevice>();

public void initConnection(){
    if(connectionThread == null){
        connectionThread = new Thread(new Runnable() {
            @Override
            public void run() {
                connectionLoop();
                connectionThread.interrupt();
                connectionThread = null;
            }
        });

        connectionThread.start();
    }
}

private void connectionLoop(){
    while(!connectionQueue.isEmpty()){
        connectionQueue.poll().connectGatt(context, false, bleInterface.mGattCallback);
        try {
            Thread.sleep(250);
        } catch (InterruptedException e) {}
    }
}
注意:发送广播(意图)可能需要这样做:

Context context = activity.getBaseContext();
context.sendBroadcast(intent);
然后广播被接收

在广播接收器中完成您想要的任何操作后,我将继续:

private Queue<BluetoothGatt> serviceDiscoveryQueue = new LinkedList<BluetoothGatt>();

private void initServiceDiscovery(){
    if(serviceDiscoveryThread == null){
        serviceDiscoveryThread = new Thread(new Runnable() {
            @Override
            public void run() {
                serviceDiscovery();

                serviceDiscoveryThread.interrupt();
                serviceDiscoveryThread = null;
            }
        });

        serviceDiscoveryThread.start();
    }
}

private void serviceDiscovery(){
    while(!serviceDiscoveryQueue.isEmpty()){
        serviceDiscoveryQueue.poll().discoverServices();
        try {
            Thread.sleep(250);
        } catch (InterruptedException e){}
    }
}
private Queue servicesdiscoveryqueue=new LinkedList();
私有void initServiceDiscovery(){
如果(serviceDiscoveryThread==null){
serviceDiscoveryThread=新线程(新可运行(){
@凌驾
公开募捐{
服务发现();
ServiceDiscoveryRead.interrupt();
serviceDiscoveryThread=null;
}
});
servicescoverythread.start();
}
}
私有void服务发现(){
而(!serviceDiscoveryQueue.isEmpty()){
serviceDiscoveryQueue.poll().discoverServices();
试一试{
睡眠(250);
}捕获(中断异常e){}
}
}
同样,在成功发现服务后,调用。同样,我向BroadcastReceiver发送了一个意图(这次使用不同的操作字符串),现在您可以开始读取、写入和启用通知/指示了。。。 P.S.如果您正在使用多台设备,请确保开始阅读、写作等。。。在所有设备都报告其服务已被发现后进行填充。

private Queue<BluetoothGattCharacteristic> characteristicReadQueue = new LinkedList<BluetoothGattCharacteristic>();

private void startThread(){

    if(initialisationThread == null){
        initialisationThread = new Thread(new Runnable() {
            @Override
            public void run() {
                loopQueues();

                initialisationThread.interrupt();
                initialisationThread = null;
            }
        });

        initialisationThread.start();
    }

}

private void loopQueues() {

    while(!characteristicReadQueue.isEmpty()){
        readCharacteristic(characteristicReadQueue.poll());
        try {
            Thread.sleep(BluetoothConstants.DELAY);
        } catch (InterruptedException e) {}
    }
    // A loop for starting indications and all other stuff goes here!
}
Context context = activity.getBaseContext();
context.sendBroadcast(intent);
public BroadcastReceiver myUpdateReceiver = new BroadcastReceiver(){

    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if(BluetoothConstants.ACTION_GATT_CONNECTED.equals(action)){
            //Connection made, here you can make a decision: do you want to initiate service discovery.
            // P.S. If you are working with multiple devices, 
            // make sure that you start the service discovery 
            // after all desired connections are made
        }
        ....
    }
}
private Queue<BluetoothGatt> serviceDiscoveryQueue = new LinkedList<BluetoothGatt>();

private void initServiceDiscovery(){
    if(serviceDiscoveryThread == null){
        serviceDiscoveryThread = new Thread(new Runnable() {
            @Override
            public void run() {
                serviceDiscovery();

                serviceDiscoveryThread.interrupt();
                serviceDiscoveryThread = null;
            }
        });

        serviceDiscoveryThread.start();
    }
}

private void serviceDiscovery(){
    while(!serviceDiscoveryQueue.isEmpty()){
        serviceDiscoveryQueue.poll().discoverServices();
        try {
            Thread.sleep(250);
        } catch (InterruptedException e){}
    }
}
private Queue<BluetoothGattCharacteristic> characteristicReadQueue = new LinkedList<BluetoothGattCharacteristic>();

private void startThread(){

    if(initialisationThread == null){
        initialisationThread = new Thread(new Runnable() {
            @Override
            public void run() {
                loopQueues();

                initialisationThread.interrupt();
                initialisationThread = null;
            }
        });

        initialisationThread.start();
    }

}

private void loopQueues() {

    while(!characteristicReadQueue.isEmpty()){
        readCharacteristic(characteristicReadQueue.poll());
        try {
            Thread.sleep(BluetoothConstants.DELAY);
        } catch (InterruptedException e) {}
    }
    // A loop for starting indications and all other stuff goes here!
}
BluetoothGatt g;
g.writeDescriptor(a);
g.writeDescriptor(b);