Хорошо, после нескольких дней пробных различных решений теперь я могу переключаться между вышеупомянутыми принтерами. Поскольку я не совсем уверен, какая из моих мер была причиной успеха, я перечислю их все, чтобы у тех, кто наткнется на этот пост, были некоторые подсказки о том, как исправить свои проблемы с Bluetooth. Однако я совершенно уверен в одном: вам не нужны разные UUID для подключения двух разных принтеров - вы можете использовать один и тот же UUID (но у меня всегда включен только один из них).
Я кэширую устройство, на которое последний раз печаталось, однако, в отличие от того, что было раньше, я больше не кэширую фактическое BluetoothDevice, вместо этого я кэширую только его mac-адрес, который можно получить через:
BluetoothDevice bluetoothDevice;
//Obtain BluetoothDevice by looking through paired devices or starting discovery
bluetoothDevice.getAddress();
getAddress() возвращает строку: аппаратный адрес устройства. Я кэширую этот mac-адрес, и в следующий раз, когда пользователь хочет напечатать, я сопоставляю кэшированный mac-адрес с mac-адресами всех сопряженных принтеров — если mac-адрес совпадает с одним из них, я пытаюсь подключиться к этому принтеру. Если это не удается, я сбрасываю свой кэшированный MAC-адрес и пытаюсь найти другое устройство, сначала проверяя свои сопряженные устройства, может ли одно из них подключиться (если я могу успешно подключиться, я соответствующим образом обновляю свой кэшированный MAC-адрес), и если это не удается, я запускаю Bluetooth Discovery ищет другие потенциальные устройства.
Теперь, чтобы не оставлять открытыми какие-либо соединения сокетов с одним из моих принтеров, моя процедура выглядит следующим образом (я не буду использовать try-catches, которые я обернул вокруг каждого вызова, чтобы облегчить чтение):
Создать сокет
BluetoothSocket btSocket = btDevice.createRfcommSocketToServiceRecord(MY_UUID);
MY_UUID относится к хорошо известному UUID, используемому для подключения к SPP-устройствам:
00001101-0000-1000-8000-00805F9B34FB
Если создание сокета терпит неудачу (что случается редко, и если это происходит, то, скорее всего, из-за недостаточных разрешений или отключенного/недоступного bluetooth), мы не можем двигаться дальше, так как нам нужен сокет для подключения. Следовательно, в вашем блоке catch вы должны активировать метод разъединения (подробнее об этом позже).
Подключиться к созданному сокету
bSocket.connect();
Если соединение не удалось, мы не можем двигаться дальше, так как нам нужно действительное соединение сокета для получения входных и выходных потоков. Следовательно, в вашем блоке catch вы должны активировать метод разъединения (подробнее об этом позже).
Получить поток ввода и вывода
Следующим шагом будет получение входных и выходных потоков из сокета. Я делаю это в цикле for, который запускается пару раз (5 раз должно быть достаточно) - на каждой итерации я проверяю, есть ли у меня выходной поток, если нет, я пытаюсь его получить, то же самое для входного потока. В конце цикла я проверяю, есть ли у меня оба потока, если да, я выхожу из цикла (И весь метод подключения), если нет, я продолжаю цикл и пробую снова. Обычно я получаю оба потока в первой итерации цикла, однако иногда мне нужно две или три итерации, чтобы получить оба потока.
Если я дойду до кода, который следует после объявления цикла, я, очевидно, не получил свои потоки или что-то еще пошло не так. В этот момент соединение считается неудачным, и я выполняю свой код отключения (который очищает открытые потоки и сокеты, подробнее об этом позже).
Чтение/запись
Теперь, когда у вас есть подключение к целевому устройству Bluetooth, вы можете выполнять операции чтения и записи. Когда вы закончите, вы должны очистить все потоки и сокеты, подробнее об этом в следующем абзаце: Отключение. Помните: если во время операций чтения/записи возникает исключение, обязательно активируйте метод отключения, чтобы очистить ваши ресурсы. Если вашему принтеру требуется какая-либо команда инициализации, обязательно отправьте ее сразу после подключения к принтеру и перед выполнением операций чтения/записи.
Отключение
Обычно есть два случая, когда вы должны отключиться:
- Как только вы закончите операции чтения/записи
- Если где-то по пути произошло исключение, чтобы очистить ваши ресурсы
Закройте свои потоки
Первое, что вы хотите сделать, это очистить свои потоки, проверить оба, ваш входной и выходной поток, если они не равны нулю, закрыть их и установить для них значение null. Обязательно завершите каждую операцию (закрытие входного потока, закрытие выходного потока и т. д.) в свой собственный try-catch, так как в противном случае неспособность выполнить одну очистку (из-за возбуждения исключения) пропустит все другие меры очистки.
Закрыть сокет
Теперь, когда вы убедились, что ваши входные потоки очищены, перейдите к закрытию соединения сокета и после этого установите для него значение null.
Еще одна вещь: у меня есть Thread.sleep в начале и в конце моего метода отключения. Тот, что в начале, длится около 2,5 секунд (= 2500 миллисекунд), цель состоит в том, чтобы убедиться, что с принтером больше ничего не происходит (например, ожидающие операции чтения/записи или принтер все еще печатает и т. д.). Второй Thread.sleep находится в конце моего метода разъединения и длится около 800 миллисекунд. Причина этого сна в конце связана с проблемами, которые у меня были, когда я пытался сразу же открыть новый сокет сразу после его закрытия. Дополнительные сведения см. в этом ответе.
Вопросы?
Если у кого-то есть вопросы, связанные с моим OP или моим ответом, дайте мне знать в комментариях, и я постараюсь ответить на них.
person
AgentKnopf
schedule
06.07.2012