BluetoothAdapter — что это, методы и управление Bluetooth в Android

Автор: IT Sectr Опубликовано: 2026-07-16 Время чтения: 10 мин

BluetoothAdapter — системный класс Android, представляющий локальный Bluetooth-адаптер devicesа. BluetoothAdapter — точка входа для всех Bluetooth-операций на Android: включение радио (enable), сканирование devices, управление видимостью (setScanMode), получение информации об адаптере (getName, getAddress, getState). Класс доступен через BluetoothManager.getAdapter() (API 18+) или BluetoothAdapter.getDefaultAdapter(). На devicesах без Bluetooth-модуля getDefaultAdapter() возвращает null. По данным Android Developers, 2026, BluetoothAdapter — обязательный компонент для любого BLE-приложения на Android, начиная с API 5.

Главное

  • BluetoothAdapter — системный класс Android для управления локальным Bluetooth-адаптером devicesа
  • getDefaultAdapter() — статический метод для получения экземпляра; возвращает null на devicesах без Bluetooth
  • getBluetoothLeScanner() — метод для доступа к современному BLE-сканеру (API 21+)
  • enable/disable — методы включения и выключения Bluetooth-радио (требуют разрешения BLUETOOTH_ADMIN)
  • startDiscovery/startLeScan — методы классического и BLE-сканирования (LE Scan устарел, используйте BluetoothLeScanner)

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

BluetoothAdapter представляет физический Bluetooth-адаптер Android-devicesа. Каждое devicesо имеет ровно один адаптер (исключение — 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, если devicesо не имеет Bluetooth-модуля (планшеты Wi-Fi-only, эмулятор). Null check обязательна: приложение должно корректно завершить работу или отключить BLE-функции.

kotlin
// Getting BluetoothAdapter (recommended)
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context

class BluetoothHelper(context: Context) {

    private val bluetoothAdapter: BluetoothAdapter?

    init {
        // Method 1: via BluetoothManager (API 18+)
        val manager = context.getSystemService(Context.BLUETOOTH_SERVICE)
                as BluetoothManager?
        bluetoothAdapter = manager?.adapter

        // Method 2: via static method (API 5+)
        // val adapter = BluetoothAdapter.getDefaultAdapter()

        // Null check
        if (bluetoothAdapter == null) {
            // Device does not support Bluetooth
        }
    }

    // Check Bluetooth state
    fun isBluetoothEnabled(): Boolean {
        return bluetoothAdapter?.isEnabled == true
    }

    // Get adapter info
    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 возвращает имя devicesа, MAC-адрес, состояние и режим видимости. Важно: на Android 10+ (API 29+) system service возвращает фиктивный 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()Режим видимости devicesаBLUETOOTH
setScanMode()Установка режима видимостиBLUETOOTH_ADMIN

Сканирование devices: startDiscovery и BluetoothLeScanner

BluetoothAdapter поддерживает два типа сканирования. Классическое Bluetooth-сканирование (BR/EDR) запускается через startDiscovery() — обнаруживает Bluetooth-devicesа всех типов, включая телефоны и гарнитуры. Результаты возвращаются через 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 сервиса, имени devicesа, MAC-адресу) и поддержка PendingIntent для фонового сканирования.

kotlin
// Old (deprecated) vs new BLE scanning API
import android.bluetooth.BluetoothAdapter
import android.bluetooth.le.*

class BLEScanner(private val bluetoothAdapter: BluetoothAdapter?) {

    // DEPRECATED: startLeScan (API 18+,   API 21)
    @Suppress("DEPRECATION")
    fun legacyScan() {
        bluetoothAdapter?.startLeScan { device, rssi, scanRecord ->
            print("Found (LE Scan): $device.name, RSSI: $rssi")
        }
    }

    // NEW: BluetoothLeScanner (API 21+)
    fun modernScan() {
        val scanner = bluetoothAdapter?.bluetoothLeScanner
            ?: return

        // Scan settings
        val settings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
            .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
            .build()

        // Filter by service (Heart Rate UUID)
        val filters = listOf(
            ScanFilter.Builder()
                .setServiceUuid(ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
                .build()
        )

        // Start scanning
        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 callback 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() — список подключённых devices, getDevicesMatchingConnectionStates() — фильтрация по состоянию. BluetoothManager также используется для получения BluetoothLeScanner на старых API.

Преимущества BluetoothManager перед прямым вызовом BluetoothAdapter.getDefaultAdapter(): приложение не зависит от статического синглтона, менеджер учитывает контекст (Activity/Application), что важно для мультиаккаунтных сценариев Android Enterprise. На Android Automotive с несколькими Bluetooth-чипами BluetoothManager.getAdapterList() возвращает все доступные адаптеры — BluetoothAdapter.getDefaultAdapter() возвращает только первый.

kotlin
// Using BluetoothManager for BLE
class BLEConnection(context: Context) {

    private val bluetoothManager: BluetoothManager =
        context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager

    private val adapter: BluetoothAdapter? = bluetoothManager.adapter

    // Get connected BLE devices list
    fun getConnectedDevices(): List<BluetoothDevice> {
        return bluetoothManager.getConnectedDevices(
            BluetoothProfile.GATT
        )
    }

    // Filter devices by state
    fun getDevicesByState(states: IntArray): List<BluetoothDevice> {
        return bluetoothManager.getDevicesMatchingConnectionStates(
            BluetoothProfile.GATT, states
        )
    }

    // Check BLE support on device
    fun isBLESupported(): Boolean {
        return adapter != null && context.packageManager
            .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
    }

    // Request Bluetooth enable via system dialog
    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-devices. isBLESupported проверяет наличие BLE-радио через PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) — важная проверка для devices с 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-devices 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
// Request Bluetooth permissions on 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
            )
        }
    }

    // Check all permissions
    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-devicesа. Пример охватывает проверку разрешений, получение адаптера, сканирование через BluetoothLeScanner и подключение через BluetoothDevice.connectGatt.

kotlin
// Full BLE manager in 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. Service discovery
    fun canScan(): Boolean {
        return adapter?.isEnabled == true
                && scanner != null
                && PermissionHelper(context).hasPermissions(context)
    }

    // 2.   with filter
    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. Stop scanning
    fun stopScanning() {
        scanner?.stopScan(object : ScanCallback() {})
    }

    // 4. Connect to BLE device
    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) {
                    // Services found, can read characteristics
                }
            },
            BluetoothDevice.TRANSPORT_LE
        )
    }

    // 5. Release resources
    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-devicesа. Получается через BluetoothManager.getAdapter() (API 18+) или BluetoothAdapter.getDefaultAdapter(). Предоставляет методы включения/выключения Bluetooth, сканирования devices, управления видимостью и получения информации об адаптере. Возвращает null на devicesах без Bluetooth-модуля.

Почему BluetoothAdapter.getDefaultAdapter() возвращает null?

Причина — отсутствие Bluetooth-радио на devicesе. Характерно для 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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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