BluetoothAdapter — що це, методи та керування Bluetooth в Android

Автор: IT Sectr Опубліковано: 2026-07-16 Час читання: 10 хв

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 — системний клас Android для керування локальним Bluetooth-адаптером пристрою
  • getDefaultAdapter() — статичний метод для отримання екземпляра; повертає null на пристроях без Bluetooth
  • getBluetoothLeScanner() — метод для доступу до сучасного BLE-сканера (API 21+)
  • enable/disable — методи ввімкнення та вимкнення Bluetooth-радіо (потребують дозволу BLUETOOTH_ADMIN)
  • startDiscovery/startLeScan — методи класичного та BLE-сканування (LE Scan застарілий, використовуйте BluetoothLeScanner)

Що таке BluetoothAdapter: суть та отримання екземпляра

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-функції.

kotlin
// Отримання 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: enable, disable, getState, getAddress

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

Сканування пристроїв: startDiscovery та BluetoothLeScanner

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 для фонового сканування.

kotlin
// Старий (застарілий) проти нового 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: сучасний API для доступу до BluetoothAdapter

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() повертає лише перший.

kotlin
// Використання 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+ без системного додатку.

Дозволи Android для роботи з BluetoothAdapter

Дозволи для 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–30API 31+
BLE-скануванняACCESS_FINE_LOCATIONBLUETOOTH_SCAN (без місцезнаходження)
Підключення до BLEACCESS_FINE_LOCATIONBLUETOOTH_CONNECT
Реклама BLEACCESS_FINE_LOCATIONBLUETOOTH_ADVERTISE
Увімкнення/вимкненняBLUETOOTH_ADMINBLUETOOTH_ADMIN (системне)
Отримання MAC-адресиACCESS_FINE_LOCATIONBLUETOOTH_CONNECT (фіктивна адреса)

На Android 12+ всі Bluetooth-дозволи є runtime-дозволами — їх потрібно запитувати в рантаймі через ActivityResultContracts.RequestMultiplePermissions. BLUETOOTH_SCAN та BLUETOOTH_ADVERTISE належать до групи NEARBY_DEVICES, BLUETOOTH_CONNECT — до тієї ж групи. Дозволи BLUETOOTH та BLUETOOTH_ADMIN залишаються в маніфесті для сумісності з API < 31, але для API 31+ вони ігноруються — Google вимагає явно вказати нові дозволи.

kotlin
// Запит дозволів 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.

Повний приклад роботи з BluetoothAdapter на Kotlin

Повний приклад BLE-додатку на Kotlin, що використовує BluetoothAdapter для сканування, підключення та читання даних BLE-пристрою. Приклад охоплює перевірку дозволів, отримання адаптера, сканування через BluetoothLeScanner та підключення через BluetoothDevice.connectGatt.

kotlin
// Повний 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 в Android?

BluetoothAdapter — клас, що представляє локальний Bluetooth-адаптер Android-пристрою. Отримується через BluetoothManager.getAdapter() (API 18+) або BluetoothAdapter.getDefaultAdapter(). Надає методи ввімкнення/вимкнення Bluetooth, сканування пристроїв, керування видимістю та отримання інформації про адаптер. Повертає null на пристроях без Bluetooth-модуля.

Чому BluetoothAdapter.getDefaultAdapter() повертає null?

Причина — відсутність Bluetooth-радіо на пристрої. Характерно для Wi-Fi-only планшетів, емулятора Android та Android TV без Bluetooth. Перевіряйте getDefaultAdapter() на null при старті додатку та вимикайте BLE-функції, якщо адаптер відсутній. Альтернатива — перевірка через PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) для більш точного визначення.

Чим BluetoothLeScanner відрізняється від startLeScan?

BluetoothLeScanner (API 21+) — сучасний API для BLE-сканування з підтримкою ScanFilter, ScanSettings та PendingIntent. startLeScan (API 18+) — застарілий метод BluetoothAdapter, що приймає LeScanCallback з обмеженим набором даних. BluetoothLeScanner рекомендується Google для всіх нових проектів, дозволяє фільтрувати за UUID, налаштовувати режим енергоспоживання та працювати в фоні через PendingIntent.

Які дозволи потрібні для BLE на Android 12+?

На Android 12+ (API 31) для BLE-сканування потрібен BLUETOOTH_SCAN, для підключення — BLUETOOTH_CONNECT, для реклами — BLUETOOTH_ADVERTISE. Дозвіл на місцезнаходження ACCESS_FINE_LOCATION більше не потрібен для BLE. На Android 6–11 необхідний ACCESS_FINE_LOCATION. Всі дозволи запитуються в рантаймі через ActivityResultContracts.

Чи можна ввімкнути Bluetooth програмно без діалогу?

З Android 10+ програмне ввімкнення Bluetooth без системного діалогу доступне лише системним додаткам з дозволом BLUETOOTH_PRIVILEGED. Звичайні додатки повинні використовувати Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) та startActivityForResult — користувач підтверджує ввімкнення в системному діалозі. BLUETOOTH_ADMIN в маніфесті не дає права на enable() на Android 10+.

Підсумки

  • BluetoothAdapter — точка входу для всіх Bluetooth-операцій на Android, отримується через BluetoothManager або getDefaultAdapter()
  • getState() та isEnabled — базові перевірки стану Bluetooth-радіо перед будь-якими BLE-операціями
  • BluetoothLeScanner (API 21+) замінює застарілий startLeScan з підтримкою ScanFilter, ScanSettings та PendingIntent
  • BluetoothManager — сучасний спосіб отримання BluetoothAdapter з підтримкою мультіакаунтних сценаріїв та Automotive
  • Дозволи еволюціонували: ACCESS_FINE_LOCATION (API 23-30) → BLUETOOTH_SCAN/CONNECT/ADVERTISE (API 31+)
  • enable()/disable() — на Android 10+ лише через системний діалог ACTION_REQUEST_ENABLE для звичайних додатків
  • connectGatt — метод BluetoothDevice для встановлення GATT-з'єднання з BLE-периферією через BluetoothGattCallback

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також