BluetoothAdapter — системний клас Android, що представляє локальний Bluetooth-адаптер пристрою. BluetoothAdapter — точка входу для всіх Bluetooth-операцій на Android: увімкнення радіо (enable), сканування пристроїв, керування видимістю (setScanMode), отримання інформації про адаптер (getName, getAddress, getState). Клас доступний через BluetoothManager.getAdapter() (API 18+) або BluetoothAdapter.getDefaultAdapter(). На пристроях без Bluetooth-модуля getDefaultAdapter() повертає null. За даними Android Developers, 2026, BluetoothAdapter — обов'язковий компонент для будь-якого BLE-додатку на Android, починаючи з API 5.
Головне
BluetoothAdapter представляє фізичний Bluetooth-адаптер Android-пристрою. Кожен пристрій має рівно один адаптер (виняток — Android Automotive з декількома Bluetooth-чіпами, де використовується BluetoothManager.getAdapterList()). BluetoothAdapter інкапсулює стан радіо: STATE_OFF (0), STATE_TURNING_ON (1), STATE_ON (2), STATE_TURNING_OFF (3). Стан відстежується через BroadcastReceiver на ACTION_STATE_CHANGED.
Отримання екземпляра BluetoothAdapter — перший крок будь-якого BLE-додатку на Android. Рекомендований метод — через BluetoothManager.getAdapter() з API 18+. Альтернатива — статичний метод BluetoothAdapter.getDefaultAdapter(), який працює з API 5, але менш гнучкий. Обидва методи повертають null, якщо пристрій не має Bluetooth-модуля (планшети Wi-Fi-only, емулятор). Перевірка на null обов'язкова: додаток повинен коректно завершити роботу або вимкнути BLE-функції.
// Отримання BluetoothAdapter (рекомендовано)
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
class BluetoothHelper(context: Context) {
private val bluetoothAdapter: BluetoothAdapter?
init {
// Метод 1: через BluetoothManager (API 18+)
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE)
as BluetoothManager?
bluetoothAdapter = manager?.adapter
// Метод 2: через статичний метод (API 5+)
// val adapter = BluetoothAdapter.getDefaultAdapter()
// Перевірка на null
if (bluetoothAdapter == null) {
// Пристрій не підтримує Bluetooth
}
}
// Перевірка стану Bluetooth
fun isBluetoothEnabled(): Boolean {
return bluetoothAdapter?.isEnabled == true
}
// Отримання інформації про адаптер
fun getAdapterInfo(): Map<String, String> {
return mapOf(
"name" to (bluetoothAdapter?.name ?: "N/A"),
"address" to (bluetoothAdapter?.address ?: "N/A"),
"state" to (bluetoothAdapter?.state?.toString() ?: "N/A"),
"scanMode" to (bluetoothAdapter?.scanMode?.toString() ?: "N/A")
)
}
}
Клас BluetoothHelper демонструє отримання BluetoothAdapter через BluetoothManager з наступною перевіркою на null. isBluetoothEnabled перевіряє isEnabled — обов'язкова умова перед будь-якими BLE-операціями. getAdapterInfo повертає ім'я пристрою, MAC-адресу, стан та режим видимості. Важливо: на Android 10+ (API 29+) системний сервіс повертає фіктивну MAC-адресу (02:00:00:00:00:00), якщо додаток не має дозволів BLUETOOTH_ADMIN та ACCESS_FINE_LOCATION.
BluetoothAdapter надає методи керування Bluetooth-радіо. enable() та disable() вмикають та вимикають Bluetooth. Обидва методи потребують дозволу BLUETOOTH_ADMIN та виконуються асинхронно: після виклику enable() система запускає процес увімкнення радіо, статус відстежується через BroadcastReceiver з екшеном BluetoothAdapter.ACTION_STATE_CHANGED. З Android 10+ enable() та disable() потребують додаткового системного дозволу — звичайні додатки не можуть програмно керувати Bluetooth без діалогу користувача.
getState() повертає поточний стан адаптера: STATE_OFF (10), STATE_TURNING_ON (11), STATE_ON (12), STATE_TURNING_OFF (13). getAddress() повертає MAC-адресу Bluetooth-адаптера. На Android 6+ для запиту MAC-адреси потрібен ACCESS_FINE_LOCATION (або ACCESS_COARSE_LOCATION для API 31+). На Android 10+ getAddress() повертає константну адресу 02:00:00:00:00:00 — реальна адреса недоступна через публічний API.
getScanMode() визначає режим видимості адаптера: SCAN_MODE_NONE (невидимий), SCAN_MODE_CONNECTABLE (видимий для підключених), SCAN_MODE_CONNECTABLE_DISCOVERABLE (видимий для всіх). Режим видимості обмежений у часі (зазвичай 60–300 секунд) для безпеки. Встановлення режиму через setScanMode() потребує BLUETOOTH_ADMIN та системного дозволу на Android 10+.
| Метод | Опис | Необхідний дозвіл |
|---|---|---|
| enable() | Увімкнення Bluetooth-радіо | BLUETOOTH_ADMIN |
| disable() | Вимкнення Bluetooth-радіо | BLUETOOTH_ADMIN |
| getState() | Поточний стан адаптера | BLUETOOTH |
| getAddress() | MAC-адреса адаптера | BLUETOOTH + ACCESS_FINE_LOCATION (API 23+) |
| getScanMode() | Режим видимості пристрою | BLUETOOTH |
| setScanMode() | Встановлення режиму видимості | BLUETOOTH_ADMIN |
BluetoothAdapter підтримує два типи сканування. Класичне Bluetooth-сканування (BR/EDR) запускається через startDiscovery() — виявляє Bluetooth-пристрої всіх типів, включаючи телефони та гарнітури. Результати повертаються через BroadcastReceiver з екшеном BluetoothDevice.ACTION_FOUND. startDiscovery() працює 12 секунд і може бути скасований викликом cancelDiscovery(). Цей метод застарілий для BLE — використовуйте BluetoothLeScanner.
BLE-сканування через BluetoothAdapter виконується застарілим методом startLeScan(LeScanCallback). Починаючи з API 21, Google рекомендує використовувати BluetoothLeScanner, отримуваний через BluetoothAdapter.getBluetoothLeScanner(). BluetoothLeScanner надає більш гнучкий API: налаштування сканування через ScanSettings (режим, callback type, match mode), фільтрація через ScanFilter (за UUID сервісу, іменем пристрою, MAC-адресою) та підтримка PendingIntent для фонового сканування.
// Старий (застарілий) проти нового BLE-сканування API
import android.bluetooth.BluetoothAdapter
import android.bluetooth.le.*
class BLEScanner(private val bluetoothAdapter: BluetoothAdapter?) {
// ЗАСТАРІЛИЙ: startLeScan (API 18+, API 21)
@Suppress("DEPRECATION")
fun legacyScan() {
bluetoothAdapter?.startLeScan { device, rssi, scanRecord ->
print("Found (LE Scan): $device.name, RSSI: $rssi")
}
}
// НОВИЙ: BluetoothLeScanner (API 21+)
fun modernScan() {
val scanner = bluetoothAdapter?.bluetoothLeScanner
?: return
// Налаштування сканування
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.build()
// Фільтрація за сервісом (UUID пульсу)
val filters = listOf(
ScanFilter.Builder()
.setServiceUuid(ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
.build()
)
// Почати сканування
scanner.startScan(filters, settings, object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
val device = result.device
val rssi = result.rssi
print("Found (BLE Scanner): ${device.name}, RSSI: $rssi, address: ${device.address}")
}
override fun onScanFailed(errorCode: Int) {
print("Scan error: $errorCode")
}
})
}
}
Клас BLEScanner порівнює застарілий startLeScan та сучасний BluetoothLeScanner. У legacyScan зворотний виклик LeScanCallback отримує BluetoothDevice, RSSI та сирий scanRecord. У modernScan використовуються ScanSettings з режимом LOW_LATENCY (максимальна швидкість виявлення) та ScanFilter для фільтрації за UUID сервісу пульсу (Heart Rate Service 0x180D). ScanCallback надає onScanResult з об'єктом ScanResult, що містить розширену інформацію: ім'я, RSSI, рекламні дані, тип з'єднання.
BluetoothManager — системний сервіс Android, введений в API 18 (Android 4.3), для керування Bluetooth-операціями. До API 18 єдиним способом отримати BluetoothAdapter був статичний метод getDefaultAdapter(). BluetoothManager надає: adapter — екземпляр BluetoothAdapter, getConnectedDevices() — список підключених пристроїв, getDevicesMatchingConnectionStates() — фільтрація за станом. BluetoothManager також використовується для отримання BluetoothLeScanner на старих API.
Переваги BluetoothManager перед прямим викликом BluetoothAdapter.getDefaultAdapter(): додаток не залежить від статичного синглтона, менеджер враховує контекст (Activity/Application), що важливо для мультіакаунтних сценаріїв Android Enterprise. На Android Automotive з декількома Bluetooth-чіпами BluetoothManager.getAdapterList() повертає всі доступні адаптери — BluetoothAdapter.getDefaultAdapter() повертає лише перший.
// Використання BluetoothManager для BLE
class BLEConnection(context: Context) {
private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val adapter: BluetoothAdapter? = bluetoothManager.adapter
// Отримати список підключених BLE-пристроїв
fun getConnectedDevices(): List<BluetoothDevice> {
return bluetoothManager.getConnectedDevices(
BluetoothProfile.GATT
)
}
// Фільтрувати пристрої за станом
fun getDevicesByState(states: IntArray): List<BluetoothDevice> {
return bluetoothManager.getDevicesMatchingConnectionStates(
BluetoothProfile.GATT, states
)
}
// Перевірити підтримку BLE на пристрої
fun isBLESupported(): Boolean {
return adapter != null && context.packageManager
.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
}
// Запит увімкнення Bluetooth через системний діалог
fun requestEnableBluetooth(activity: MainActivity) {
if (adapter?.isEnabled == false) {
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
activity.startActivityForResult(intent, REQUEST_ENABLE_BT)
}
}
companion object {
const val REQUEST_ENABLE_BT = 1001
}
}
Клас BLEConnection використовує BluetoothManager для доступу до BluetoothAdapter та отримання списку підключених GATT-пристроїв. isBLESupported перевіряє наявність BLE-радіо через PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) — важлива перевірка для пристроїв з Bluetooth Classic без BLE. requestEnableBluetooth показує системний діалог увімкнення Bluetooth (ACTION_REQUEST_ENABLE), не потребуючи дозволу BLUETOOTH_ADMIN — це єдиний легальний спосіб увімкнення Bluetooth на Android 10+ без системного додатку.
Дозволи для BluetoothAdapter еволюціонували з кожною версією Android. На Android 6–11 (API 23–30) для BLE-сканування обов'язкові BLUETOOTH, BLUETOOTH_ADMIN та ACCESS_FINE_LOCATION. На Android 12+ (API 31+) Google розділив дозволи: ACCESS_FINE_LOCATION замінено на BLUETOOTH_SCAN (сканування), BLUETOOTH_CONNECT (підключення), BLUETOOTH_ADVERTISE (реклама). Для пошуку BLE-пристроїв BLUETOOTH_SCAN достатньо, місцезнаходження не потрібне.
Таблиця дозволів за версіями Android:
| Операція | API 23–30 | API 31+ |
|---|---|---|
| BLE-сканування | ACCESS_FINE_LOCATION | BLUETOOTH_SCAN (без місцезнаходження) |
| Підключення до BLE | ACCESS_FINE_LOCATION | BLUETOOTH_CONNECT |
| Реклама BLE | ACCESS_FINE_LOCATION | BLUETOOTH_ADVERTISE |
| Увімкнення/вимкнення | BLUETOOTH_ADMIN | BLUETOOTH_ADMIN (системне) |
| Отримання MAC-адреси | ACCESS_FINE_LOCATION | BLUETOOTH_CONNECT (фіктивна адреса) |
На Android 12+ всі Bluetooth-дозволи є runtime-дозволами — їх потрібно запитувати в рантаймі через ActivityResultContracts.RequestMultiplePermissions. BLUETOOTH_SCAN та BLUETOOTH_ADVERTISE належать до групи NEARBY_DEVICES, BLUETOOTH_CONNECT — до тієї ж групи. Дозволи BLUETOOTH та BLUETOOTH_ADMIN залишаються в маніфесті для сумісності з API < 31, але для API 31+ вони ігноруються — Google вимагає явно вказати нові дозволи.
// Запит дозволів Bluetooth на Android 12+
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.content.ContextCompat
class PermissionHelper(context: Context) {
fun getRequiredPermissions(): Array<String> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12+: BLE-
arrayOf(
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_ADVERTISE
)
} else {
// Android 6-11: BLE
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN
)
}
}
// Перевірка всіх дозволів
fun hasPermissions(context: Context): Boolean {
return getRequiredPermissions().all { permission ->
ContextCompat.checkSelfPermission(context, permission)
== PackageManager.PERMISSION_GRANTED
}
}
}
Клас PermissionHelper повертає коректний набір дозволів залежно від API Level. На Android 12+ використовуються BLUETOOTH_SCAN, BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE без місцезнаходження. На Android 6–11 для BLE сканування все ще потрібен ACCESS_FINE_LOCATION. Розробнику необхідно враховувати обидва сценарії при запиті дозволів через ActivityResultContracts або RxPermissions.
Повний приклад BLE-додатку на Kotlin, що використовує BluetoothAdapter для сканування, підключення та читання даних BLE-пристрою. Приклад охоплює перевірку дозволів, отримання адаптера, сканування через BluetoothLeScanner та підключення через BluetoothDevice.connectGatt.
// Повний BLE-менеджер на Kotlin
class BLEManager(private val context: Context) {
private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val adapter: BluetoothAdapter? = bluetoothManager.adapter
private var scanner: BluetoothLeScanner? = adapter?.bluetoothLeScanner
private var gatt: BluetoothGatt? = null
// 1. Виявлення сервісів
fun canScan(): Boolean {
return adapter?.isEnabled == true
&& scanner != null
&& PermissionHelper(context).hasPermissions(context)
}
// 2. з фільтром
fun startScanning(callback: (BluetoothDevice, Int) -> Unit) {
if (!canScan()) return
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0)
.build()
scanner?.startScan(null, settings, object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
callback(result.device, result.rssi)
}
})
}
// 3. Зупинка сканування
fun stopScanning() {
scanner?.stopScan(object : ScanCallback() {})
}
// 4. Підключення до BLE-пристрою
fun connectToDevice(device: BluetoothDevice) {
if (adapter?.isEnabled != true) return
gatt = device.connectGatt(
context,
false,
object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices()
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
// Сервіси знайдено, можна читати характеристики
}
},
BluetoothDevice.TRANSPORT_LE
)
}
// 5. Звільнення ресурсів
fun disconnect() {
gatt?.disconnect()
gatt?.close()
gatt = null
}
}
Менеджер BLEManager об'єднує повний BLE-цикл на Android: перевірка адаптера та дозволів (canScan), сканування через BluetoothLeScanner з ScanSettings (startScanning), підключення через BluetoothDevice.connectGatt з TRANSPORT_LE (connectToDevice), звільнення ресурсів (disconnect). Всі BLE-операції виконуються на UI-потоці — Android викликає callbackи BluetoothGattCallback на основному потоці. Для продуктивних BLE-задач рекомендується виносити GATT-операції у фоновий HandlerThread.
Часті запитання
BluetoothAdapter — клас, що представляє локальний Bluetooth-адаптер Android-пристрою. Отримується через BluetoothManager.getAdapter() (API 18+) або BluetoothAdapter.getDefaultAdapter(). Надає методи ввімкнення/вимкнення Bluetooth, сканування пристроїв, керування видимістю та отримання інформації про адаптер. Повертає null на пристроях без Bluetooth-модуля.
Причина — відсутність Bluetooth-радіо на пристрої. Характерно для Wi-Fi-only планшетів, емулятора Android та Android TV без Bluetooth. Перевіряйте getDefaultAdapter() на null при старті додатку та вимикайте BLE-функції, якщо адаптер відсутній. Альтернатива — перевірка через PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) для більш точного визначення.
BluetoothLeScanner (API 21+) — сучасний API для BLE-сканування з підтримкою ScanFilter, ScanSettings та PendingIntent. startLeScan (API 18+) — застарілий метод BluetoothAdapter, що приймає LeScanCallback з обмеженим набором даних. BluetoothLeScanner рекомендується Google для всіх нових проектів, дозволяє фільтрувати за UUID, налаштовувати режим енергоспоживання та працювати в фоні через PendingIntent.
На Android 12+ (API 31) для BLE-сканування потрібен BLUETOOTH_SCAN, для підключення — BLUETOOTH_CONNECT, для реклами — BLUETOOTH_ADVERTISE. Дозвіл на місцезнаходження ACCESS_FINE_LOCATION більше не потрібен для BLE. На Android 6–11 необхідний ACCESS_FINE_LOCATION. Всі дозволи запитуються в рантаймі через ActivityResultContracts.
З Android 10+ програмне ввімкнення Bluetooth без системного діалогу доступне лише системним додаткам з дозволом BLUETOOTH_PRIVILEGED. Звичайні додатки повинні використовувати Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) та startActivityForResult — користувач підтверджує ввімкнення в системному діалозі. BLUETOOTH_ADMIN в маніфесті не дає права на enable() на Android 10+.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також