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 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() — списък на свързани устройства, 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 разрешения са разрешения по време на изпълнение — трябва да се искат по време на изпълнение чрез 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. На 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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също