Соединение Bluetooth RFCOMM не может быть установлено последовательно на Android 4.2
У меня есть приложение, которое разговаривает с пользовательским устройством через RFCOMM через Bluetooth. Код связи основан на примере проекта BluetoothTalk. Он работал без каких-либо проблем раньше на Galaxy S3, Galaxy S2, Galaxy Note и Nexus 7.
Недавно Nexus 7 был обновлен до Android 4.2, и с тех пор проблема происходит следующим образом:
-
Когда вы используете приложение для настройки соединения в первый раз, что означает, что устройство только что включено, а приложение только что запущено, никаких проблем, вы можете получить данные в обычном режиме.
-
Затем, если вы остановите связь и попытаетесь перезапустить, сообщение завершится с ошибкой "java.io.IOException: bt socket closed, read return: -1". С этого момента, независимо от того, сколько раз вы пытаетесь восстановить соединение, он просто всегда терпит неудачу.
-
Единственный способ заставить его работать снова - перезагрузить пользовательское устройство и приложение, а затем попытаться подключиться, общение станет нормальным. Но затем, как только вы остановите и перезапустите сообщение, он продолжает терпеть неудачу.
Я позаимствовал Nexus 4 с Android 4.2, и проблема остается.
Это действительно раздражает, потому что основная ценность нашего устройства зависит от приложения RFCOMM Bluetooth. Я дважды проверял документацию на BT в Android 4.2 и не видел никаких существенных изменений. Я довольно уверен в том, что код на моей стороне, потому что он работает для любого Android-устройства, которое не работает 4.2
Любые намеки или предложения были бы весьма полезны. Устройство необходимо демонтировать в самом начале декабря, и мы действительно хотим решить эту проблему как можно скорее.
EDIT: теперь, когда был выпущен 4.2.1, и проблема все еще не решена. Можем ли мы хотя бы получить подтверждение относительно того, будет ли оно работать и скоро будет исправлено?
Ответы
Ответ 1
Я наконец понял решение.
Оказывается, это не ошибка в новом Bluetooth-драйвере. Это угловой случай, который не рассматривается в примере с образцом Bluetooth, в частности, проект BluetoothChat, предоставляемый Android SDK.
В служебном коде Bluetooth внутри проекта BluetoothChat, когда соединение проваливается или теряется, он всегда запускает службу для перезапуска режима прослушивания. Это не проблема, когда телефон используется как сервер, однако в некоторых сценариях, когда телефон используется как клиент, как только он переходит в режим прослушивания, вы не можете подключиться к другим серверам. Потому что в функции "connect" она не отменяет (In) SecureAcceptThread. Таким образом, вы слушаете другие соединения как сервер, пытаясь подключиться к другим серверам в качестве клиента. Это противоречиво.
Способом решения этой проблемы является то, что если вы уверены, что ваш телефон не будет использоваться в качестве сервера, который будет прослушивать входящее соединение, просто удалите этот код, чтобы перезапустить режим прослушивания после того, как соединение было неудачно или остановлено.
Ответ 2
Это не поможет вам, но обратите внимание, что Google представил совершенно новый стек Bluetooth с 4.2.
Это должно быть хорошо - по моему опыту как пользователя и разработчика Android с Bluez (старый комбо) никогда не работал надежно, поэтому я был очень рад услышать, что они взяли на себя полную переписку.
Я думаю, все, что я могу сказать, это то, что кажется, что вы столкнулись с ошибкой или причудой в новом стеке. К сожалению, есть проблемы с новым стеком.
Что касается вашей демонстрации, обратите внимание, что Google публикует изображения прошивки для всех своих устройств Nexus (https://developers.google.com/android/nexus/images) и довольно легко мигает ими на ваших устройствах.
Поэтому я предлагаю вам записать отчет об ошибке, а затем запустил устройства на 4.1.2.
Ответ 3
У меня была аналогичная проблема, и я потратил некоторое время на отладку этой проблемы. Я запускаю Android 4.3 и нашел единственную базовую модификацию, которая требовалась для кода образца BluetoothChat:
MY_UUID_SECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
Я пытаюсь подключиться к модулю bluetooth HC-05, и я считаю, что он использует профиль SPP, как указано в этом UUID. Вы можете запросить UUID устройства с помощью этой строки кода:
UUID uuid = device.getUuids()[0].getUuid();
Проверьте UUID и посмотрите, совпадает ли он с профилем, который имеет смысл, в зависимости от типа устройства, к которому вы подключаетесь.
MY_UUID_SECURE = uuid;
UUID может указывать другой тип профиля устройства, в зависимости от того, к чему вы подключаетесь. При правильном UUID я все еще мог запускать код AcceptThread и слушать как BluetoothServerSocket без каких-либо проблем. Надеюсь, это поможет, однако, я относительно новичок в разработке Android и Bluetooth, поэтому, если мои предположения ошибочны, пожалуйста, исправьте меня.
Ответ 4
Обнаружены аналогичные проблемы. Исправлены некоторые из них путем создания с 4.2 SDK с 17 в качестве цели сборки.
Ответ 5
Встречается с той же проблемой с моим Nexus 4.
В моем случае, я думаю, проблема заключается в протоколе обнаружения службы SDP. Устройство и сервер должны использовать один и тот же UUID для определенной службы.
Я использую SPP (протокол последовательного порта) в своем приложении. Поэтому я изменяю UUID с того, что находится в примере кода BluetoothChat как "fa87c0d0-afac-11de-8a39-0800200c9a66" на SPP "00001101-0000-1000-8000-00805F9B34FB". Теперь это нормально.
И я отключу код прослушивания для acceptThread в BluetoothChatService.start(). Теперь более кратки. Надеюсь, это поможет.
Ответ 6
Это случилось со мной и в моих тестах. Я пример кода BluetoothChat вы должны посмотреть на метод connectionLost. Я не помню, есть ли какая-либо переменная, которая поддерживает количество потерянных соединений, но вы можете добавить это сами. в методе connectionLost, проверьте, меньше ли количество потерянных соединений, чем предопределенное число (в моем случае 3). Если это правда, отправьте сообщение в пользовательский интерфейс с помощью mHandler (тост) и снова вызовите connect (device). Если это неверно (вы потеряли соединение более 3 раз), вызовите метод stop().
Также не забудьте открыть сокет в ConnectThread следующим образом:
public ConnectThread(BluetoothDevice device, boolean isSecure) {
mmDevice = device;
BluetoothSocket tmp = null;
mSocketType = isSecure ? "Secure" : "Insecure";
// Get a BluetoothSocket for a connection with the given BluetoothDevice
if (isSecure) {
// reflection is better to use
Method m = null;
try {
Log.d(TAG, "create reflection");
m = device.getClass().getMethod("createRfcommSocket",new Class[] { int.class });
} catch (NoSuchMethodException e1) {
e1.printStackTrace();
}
try {
tmp = (BluetoothSocket) m.invoke(device, 1);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
mmSocketFallBack = tmp;
} else {
Log.d(TAG, "create insecure");
try {
tmp = device
.createInsecureRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
e.printStackTrace();
}
}
mmSocket = mmSocketFallBack;
}
Ваше соединениеLost должно выглядеть примерно так:
public void connectionLost() {
init = false;
Log.d(TAG, "connectionLost -> " + mConnectionLostCount);
mConnectionLostCount++;
if (mConnectionLostCount < 3) {
// Send a reconnect message back to the Activity
Message msg = mHandler.obtainMessage(cBluetooth.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(WebAppInterface.TOAST, "Connection lost. Reconnecting...");
msg.setData(bundle);
mHandler.sendMessage(msg);
connect(mSavedDevice,true);
} else {
mConnectionLostCount = 0;
Message msg = mHandler.obtainMessage(cBluetooth.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(WebAppInterface.TOAST,"Device connection was lost!");
msg.setData(bundle);
mHandler.sendMessage(msg);
cBluetooth.this.stop();
}
}
Надеюсь, вы сможете приспособить его к своему делу. Вы также можете проверить эти ссылки, они мне очень помогли:
Ответ 7
У меня такой же опыт в 4.2.2.
Но я обнаружил, что стек bluetooth начинает плохо себя вести только после того, как мое приложение падает или убивается без должной очистки ресурсов (сокет и/или потоки). До этого, когда я закрываю приложение правильно, он отлично работает.
Например, когда я закрываю сокет, а затем убиваю приложение, я могу запустить его и снова подключиться. Если я убью свое приложение без предварительного закрытия сокета, это проклят, и я должен перезагрузить устройство, чтобы он снова работал.
Итак, кажется, что новый стек Android bluetooth имеет некоторую ошибку в неявном механизме очистки. Когда приложение падает, Android должен очистить открытые ресурсы bluetooth, чего не происходит.
Мой код управления bluetooth находится в подклассе Application. Невозможно - или я не вижу одного - как подключить приложение к уничтожению и выполнить очистку.
Любое предложение оценено
EDIT:
Я сделал довольно грязное обходное решение.
Я создал отдельное приложение, содержащее один удаленный сервис. Я переместил весь этот код связи (открытие сокетов и потоков, чтение, запись, закрытие) в эту удаленную службу. Затем, если основное приложение выйдет из строя или будет убито, служба все еще работает. Когда я снова запустил приложение, соединение все еще продолжается.
Байты, считываемые из входного потока bluetooth, передаются в основное приложение через стандартную службу обмена сообщениями. Для моего случая это нормально, поскольку я переношу очень маленькие объемы данных.
Тем не менее, когда приложение, содержащее службу, убивается, происходит тот же самый беспорядок.
Ответ 8
Я столкнулся с той же проблемой. Это работает для меня:
try {
Thread.sleep(1000);
}
catch(Exception e3)
{
Toast.makeText(getApplicationContext(), "wa ni sud sa thread sleep!", Toast.LENGTH_SHORT).show();
}
btSocket.close();