Реализация подключения к принтеру через Bluetooth из мобильного приложения
Bluetooth-печать выглядит просто: найти устройство, подключиться, отправить байты. На практике — сканирование не находит принтер, потому что он уже сопряжён с другим телефоном; соединение рвётся в середине печати без уведомления; на Android 12+ разрешения Bluetooth изменились и старый код не компилируется.
Android: классический Bluetooth vs BLE
Термопринтеры (Zebra ZQ300, Bixolon SPP-R310, iDPRT SP450) работают через Bluetooth Classic (SPP — Serial Port Profile). Не BLE. Это важно: API разные.
Классический Bluetooth на Android — BluetoothAdapter, BluetoothDevice, BluetoothSocket. С Android 12 (targetSdk 31+) нужны новые разрешения:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Без BLUETOOTH_SCAN с флагом neverForLocation Google Play требует обоснование — зачем Bluetooth-сканирование для определения местоположения. Флаг явно говорит, что не для этого.
Соединение через SPP UUID:
val device: BluetoothDevice = bluetoothAdapter.bondedDevices
.firstOrNull { it.name.contains("Zebra") } ?: return
val socket = device.createRfcommSocketToServiceRecord(
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") // SPP UUID
)
withContext(Dispatchers.IO) {
socket.connect()
val outputStream = socket.outputStream
outputStream.write(zplData)
outputStream.flush()
}
createRfcommSocketToServiceRecord может бросить IOException если устройство занято другим соединением. Zebra-принтеры поддерживают только одно активное соединение — если принтер уже подключён к другому телефону, соединение упадёт с ошибкой. Нужно показывать пользователю внятное сообщение, а не стандартный крэш.
Поиск устройств. BluetoothAdapter.startDiscovery() — асинхронный, занимает до 12 секунд, разряжает батарею. Лучше показывать список уже сопряжённых устройств (bondedDevices) — пользователь сопрягает принтер один раз в настройках телефона. Поиск новых — только по явному запросу.
Zebra Link-OS SDK: когда простого Bluetooth мало
Для Zebra-принтеров есть официальный SDK — ZSDK_ANDROID_API_x.x.aar. Он абстрагирует транспорт (Bluetooth / TCP) и добавляет полезное:
- Проверка статуса принтера перед печатью (
PrinterStatus) - Получение конфигурации (
SettingsGenerator.getConfigLabel()) - Калибровка носителя
- Получение список шрифтов и форматов на принтере
val connection = BluetoothConnection(macAddress)
connection.open()
val printer = ZebraPrinterFactory.getInstance(connection)
val status = printer.currentStatus
if (status.isReadyToPrint) {
printer.sendCommand(zplTemplate)
} else {
// status.isPaused, status.isHeadOpen, status.isPaperOut — конкретная причина
showError(getPrinterStatusMessage(status))
}
connection.close()
Без SDK — нет способа узнать, что в принтере закончилась бумага до попытки печати. С SDK — status.isPaperOut даёт конкретную причину отказа.
iOS: ExternalAccessory framework
На iOS термопринтеры с Bluetooth Classic работают через ExternalAccessory framework — MFi (Made for iPhone) протокол. Принтер должен иметь MFi-сертификацию. Zebra, Star Micronics, Bixolon — сертифицированы.
import ExternalAccessory
let session = EASession(accessory: accessory, forProtocol: "com.zebra.rawport")
session?.outputStream?.schedule(in: .main, forMode: .default)
session?.outputStream?.open()
let data = zplString.data(using: .utf8)!
data.withUnsafeBytes { session?.outputStream?.write($0, maxLength: data.count) }
Protocol string (com.zebra.rawport) — специфичен для вендора, прописывается в Info.plist под ключом UISupportedExternalAccessoryProtocols. Без этого iOS не даст открыть сессию.
BLE-принтеры на iOS — без MFi-ограничений, через стандартный CoreBluetooth. Star Micronics mPOP, некоторые модели Bixolon — поддерживают BLE.
Стабильность соединения и переподключение
Bluetooth-соединение рвётся. Принтер выключили и включили. Телефон отошёл за пределы радиуса. Реализация без авто-реконнекта — источник жалоб в поддержку.
Паттерн: при IOException на outputStream.write() — закрыть сокет, подождать 1–2 секунды, попробовать переподключиться (максимум 3 попытки). Если не удалось — сохранить задание в локальную очередь, уведомить пользователя. WorkManager с BackoffPolicy.LINEAR для повторных попыток при следующем доступном соединении.
Реализация Bluetooth-печати с поддержкой Zebra/Star/Bixolon: 1–2 недели. Стоимость рассчитывается индивидуально.







