BluetoothAdapter — системска класа Андроида која представља локални Bluetooth-адаптер уређаја. BluetoothAdapter је улазна тачка за све Bluetooth-операције на Андроиду: укључивање радија (enable), скенирање уређаја, управљање видљивошћу (setScanMode), добијање информација о адаптеру (getName, getAddress, getState). Класа је доступна преко BluetoothManager.getAdapter() (API 18+) или BluetoothAdapter.getDefaultAdapter(). На уређајима без Bluetooth-модула getDefaultAdapter() враћа null. Према Android Developers, 2026, BluetoothAdapter је обавезна компонента за сваку BLE-апликацију на Андроиду, почев од 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-апликације на Андроиду. Препоручени метод — преко 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 за позадинско скенирање.
// Стари (застарели) vs нови 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 — системски сервис Андроида, уведен у 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 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, локација није потребна.
Табела дозвола по верзијама Андроида:
| Операција | 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 нивоа. На 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-циклус на Андроиду: проверу адаптера и дозвола (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 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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође