Как хорошо выполнять потоковую передачу данных в режиме реального времени с помощью Java Android SDK

У меня есть самодельное устройство Bluetooth, измеряющее ЭКГ с частотой 500 Гц: каждые 2 мс устройство отправляет 9 байтов данных (заголовок, измерение ЭКГ, нижний колонтитул). Так что это примерно 9 * 500 = 4,5 Кбайт / с потока данных.

У меня есть программа C ++ для Windows, способная подключить устройство и получить поток данных (отображая его с помощью Qt / qwt). В этом случае я использую панель управления Windows для подключения устройства и подключаю его через виртуальный COM-порт с использованием интерфейса boost serial_port. Это работает отлично, и я получаю свой поток данных в режиме реального времени: я получаю точку измерения каждые 2 мс или около того.

Я портировал всю программу на Android через QtCreator 3.0.1 (Qt 5.2.1). Похоже, что виртуальные COM-порты не могут быть доступны с помощью boost (возможно, разрешения SDK этого не позволят), поэтому я написал фрагмент кода Java для открытия и управления соединением Bluetooth. Таким образом, мое приложение остается C ++ / Qt, но только слой, соединяющий и считывающий данные с устройства, был переработан в Java (открытие соединения с помощью createInsecureRfcommSocketToServiceRecord):

Java-код для чтения данных:

public int readData( byte[] buffer )
{
    if( mInputStream == null )
    {
        traceErrorString("No connection, can't receive data");
    }
    else
    {
        try
        {
            final boolean verbose = false;

            int available = mInputStream.available();

            if ( verbose )
            {
                Calendar c = Calendar.getInstance();
                Date date = new Date();
                c.setTime(date);
                c.get(Calendar.MILLISECOND);

                SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
                String currentTime = sdf.format(date);

                traceDebugString( currentTime + ":" + c.get(Calendar.MILLISECOND) + " - " + available + " bytes available, requested " + buffer.length );
            }

            if ( available >= buffer.length )
                return mInputStream.read( buffer ); // only call read if we know it's not blocking
            else
                return 0;
        }
        catch (IOException e)
        {
            traceDebugString( "Failed to read data...disconnected?" );
        }
    }

    return -1;
}

Вызывается из C ++ так:

bool ReceiveData( JNIEnv* env,
                  char* data,
                  size_t length,
                  bool& haserror )
{
    bool result = false;

    jbyteArray array = env->NewByteArray(length);
    jint res = env->CallIntMethod(j_object, s_patchIfReceiveDataID, array );
    if ( static_cast<size_t>(res) == length )
    {
        env->GetByteArrayRegion(array, 0, length, reinterpret_cast<jbyte*>(data));

        result = true;
    }
    else if ( res == -1 )
    {
        haserror = true;
    }
    else
    {
        // not enough data in the stream buffer
        haserror = false;
    }

    return result;
}


bool readThread( size_t blockSize )
{
    BTGETANDCHECKENV // retrieving environment

    char* buf = new char[blockSize];
    bool haserror = false;
    while ( !haserror )
    {
        if ( !ReceiveData( env, buf, blockSize, haserror ) )
        {
            // could not read data
            if ( haserror )
            {
                // will stop this thread soon
            }
            else
            {
                boost::this_thread::sleep( boost::posix_time::milliseconds( 10 ) );
            }
        }
    }
    delete [] buf;

    return true;
}

Это работает довольно хорошо ...за пять первых секунд Я получаю значения в режиме реального времени, а затем:

Иногда он останавливается навсегда, что означает, что значение mInputStream.available () остается ниже запрошенного.Иногда он останавливается только на секунду или около того, а затем продолжается, но данные принимаются блоками ~ 1 секунды. Значение mInputStream.available () может перемещаться от 0 до более 3000 между двумя вызовами (по истечении 10 мс). На самом деле, я вижу то же самое в течение 5 первых секунд, но доступность буфера никогда не превышает 150 байт, через 5 секунд он может достигать 3000 байт.

Вот как может выглядеть журнал, если для verbose установлено значение true:

14:59:30:756 - 0 bytes available, requested 3
14:59:30:767 - 0 bytes available, requested 3
14:59:30:778 - 0 bytes available, requested 3
14:59:30:789 - 1728 bytes available, requested 3
14:59:30:790 - 1725 bytes available, requested 6
14:59:30:792 - 1719 bytes available, requested 3

Мое устройство ЭКГ определенно не отправило 1728 байт за 11 мс !!

Я знаю, что мое устройство отправляет 9 байтов каждые 2 мс (в противном случае оно не будет работать на моем ПК-приложении). Похоже, что Java делает некоторую неожиданную буферизацию и не делает доступными 9 байтов каждые 2 мс ... Также странно, что поначалу все работает нормально только в течение 5 секунд.

Обратите внимание, что я пытался использовать read (), не проверяя available () (блокирующую версию), но испытал точно такое же поведение.

Вот и мне интересно, что я делаю не так ...

Есть ли способ заставить поток ввода Java обновить себя?Есть ли способ попросить Java продолжить ожидающие события (как у нас QApplication :: processEvents)?Есть ли какие-либо глобальные настройки для указания размеров буфера для потоков (я не нашел ни одного на уровне BluetoothDevice / BluetoothSocket)На ПК, при открытии виртуального COM-порта, я должен указать скорость передачи, стоп-бит, квитирование и тому подобное. На Android я просто открываю гнездо Rfcomm без опции, может ли это быть проблемой (тогда устройство ЭКГ и смартфон не будут синхронизированы ...)?

Любая помощь или идея будет приветствоваться!

Изменить: я испытываю, что на телефоне Nexus 5, Android 4.4.2 я только что протестировал один и тот же пакет apk на разных устройствах:

Galaxy S4 с Android 4.4.2: та же проблема.Galaxy S3 с пользовательским CyanogenMod 11 Android 4.4.2: потоковая передача данных кажется идеальной, не происходит замораживание через 5 секунд, а данные поступают в режиме реального времени .... похоже, что вся система способна достичь того, чего я хочу, но выглядит так Настройка Android по умолчанию делает вещи слишком медленными .... не знаю, если на уровне ОС можно изменить настройку, чтобы решить эту проблему.

Редактировать: Поскольку я не получил ответа :-( Я пытался сделать то же самое, используя чистую программу на Java (без C ++, без Qt). Была такая же проблема:Потоковая передача данных Bluetooth SPP в реальном времени на Android работает только в течение 5 секунд.

Ответы на вопрос(1)

Ваш ответ на вопрос