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 представляет физический 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-функции.
// 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 предоставляет методы управления 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 |
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 для фонового сканирования.
// 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 — системный сервис 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() возвращает только первый.
// 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+ без системного приложения.
Разрешения для 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–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 требует явно указать новые разрешения.
// 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.
Полный пример BLE-приложения на Kotlin, использующего BluetoothAdapter для сканирования, подключения и чтения данных BLE-devicesа. Пример охватывает проверку разрешений, получение адаптера, сканирование через BluetoothLeScanner и подключение через BluetoothDevice.connectGatt.
// 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 — класс, представляющий локальный Bluetooth-адаптер Android-devicesа. Получается через BluetoothManager.getAdapter() (API 18+) или BluetoothAdapter.getDefaultAdapter(). Предоставляет методы включения/выключения Bluetooth, сканирования devices, управления видимостью и получения информации об адаптере. Возвращает null на devicesах без Bluetooth-модуля.
Причина — отсутствие Bluetooth-радио на devicesе. Характерно для 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также