BluetoothLeScanner — Android osztály Bluetooth Low Energy eszközök szkennelésére, elérhető az API 21-től (Android 5.0). A BluetoothLeScanner felváltotta az elavult startLeScan metódust a BluetoothAdapteren, rugalmas API-t biztosítva szkennelési beállításokkal (ScanSettings), szűréssel (ScanFilter) és háttérmód támogatással (PendingIntent). A példány a BluetoothAdapter.getBluetoothLeScanner() segítségével szerezhető meg. A Android Developers, 2026 szerint a BluetoothLeScanner három energiafogyasztási módot támogat és lehetővé teszi a BLE reklámcsomagok szkennelését szolgáltatás UUID, eszköznév vagy MAC-cím szerinti szűréssel.
Főbb pontok
BluetoothLeScanner — rendszerosztály a BLE-szkennelés kezelésére Androidban. Ellentétben a BluetoothAdapter.startLeScan()-nel, amely egy egyszerű LeScanCallback-et fogad, a BluetoothLeScanner objektumorientált API-t biztosít beállításokkal, szűrőkkel és kiterjesztett hibakezeléssel. Az osztály az API 21-ben (Android 5.0) jelent meg a BLE 4.2 támogatásával együtt, és a BLE-szkennelés fő módszere maradt az Android összes modern verzióján.
A BluetoothLeScanner példányának beszerzése a BluetoothAdapter.getBluetoothLeScanner() segítségével történik. A metódus null-t ad vissza, ha a Bluetooth-adapter nem elérhető (Bluetooth kikapcsolva vagy az eszköz nem támogatja a BLE-t). Beszerzés előtt ellenőrizze a BluetoothAdapter.isEnabled()-et és a FEATURE_BLUETOOTH_LE meglétét a PackageManageren keresztül. A szkenner beszerzése után bármely szálban elindíthatja a szkennelést — az Android maga ütemezi a BLE-műveleteket a Bluetooth-verem belső szálán.
// BluetoothLeScanner beszerzése
class BLEScannerManager(context: Context) {
private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val adapter: BluetoothAdapter? = bluetoothManager.adapter
private var scanner: BluetoothLeScanner? = null
fun initScanner(): Boolean {
// BLE elérhetőségének ellenőrzése
if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
return false
}
// Bluetooth bekapcsolásának ellenőrzése
if (adapter?.isEnabled != true) {
return false
}
// Szkenner beszerzése
scanner = adapter?.bluetoothLeScanner
return scanner != null
}
// Szkenner elérhetőségének ellenőrzése
val isAvailable: Boolean
get() = scanner != null
// Alap szkennelés indítása szűrők nélkül
fun startBasicScan() {
scanner?.startScan(object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
handleResult(result)
}
})
}
private fun handleResult(result: ScanResult) {
val device = result.device
print("Device: ${device.name ?: "Unnamed"}, RSSI: ${result.rssi}, address: ${device.address}")
}
}
A BLEScannerManager osztály a BluetoothLeScanner biztonságos beszerzését és inicializálását mutatja be. Az initScanner ellenőrzi a BLE meglétét a hasSystemFeature segítségével, a Bluetooth bekapcsolását és a szkenner sikeres beszerzését. A startBasicScan elindítja a szkennelést beállítások és szűrők nélkül — érzékeli az összes BLE-eszközt a hatókörben. A handleResult elemzi a ScanResult-ot: BluetoothDevice (név, cím), RSSI (jelerősség), scanRecord (reklámadatok).
ScanSettings — osztály a BLE-szkennelés konfigurálásához. A fő paraméter — a szkennelési mód (scanMode), amely meghatározza az energiafogyasztás és az érzékelési késleltetés közötti kompromisszumot. A ScanSettings.Builder lehetővé teszi a következők konfigurálását: scanMode, callbackType (CALLBACK_TYPE_ALL_MATCHES, CALLBACK_TYPE_FIRST_MATCH, CALLBACK_TYPE_MATCH_LOST), matchMode (MATCH_MODE_AGGRESSIVE, MATCH_MODE_STICKY), reportDelay (küldeményes küldés késleltetése) és phy (PHY_LE_1M, PHY_LE_2M, PHY_LE_CODED).
Három szkennelési mód: SCAN_MODE_LOW_POWER (0) — háttér-szkennelés alacsony energiafogyasztással, érzékelési késleltetés néhány másodperc. SCAN_MODE_BALANCED (1) — kiegyensúlyozott mód a legtöbb forgatókönyv számára. SCAN_MODE_LOW_LATENCY (2) — minimális érzékelési késleltetés (körülbelül 100 ms), maximális energiafogyasztás. Aktív eszközkereséshez használja a LOW_LATENCY-t, háttér-figyeléshez a LOW_POWER-t.
reportDelay — késleltetés ezredmásodpercben az eredmények csoportos küldése előtt. Ha a reportDelay = 0, az eredmények az érzékelés után azonnal elküldésre kerülnek. Ha > 0, az Android összegyűjti az eredményeket és küldeményt küld az onBatchScanResults-on keresztül. A küldeményes küldés csökkenti a callback-hívások számát és az energiafogyasztást, alkalmas alacsony prioritású háttér-szkenneléshez.
// ScanSettings konfiguráció különböző forgatókönyvekhez
class ScanSettingsProvider {
// 1. Gyors szkennelés (aktív keresés)
fun lowLatencyScan(): ScanSettings {
return ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.setReportDelay(0)
.setPhy(ScanSettings.PHY_LE_ALL_SUPPORTED)
.build()
}
// 2. Energiatakarékos szkennelés (háttér-figyelés)
fun lowPowerScan(): ScanSettings {
return ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
.setMatchMode(ScanSettings.MATCH_MODE_STICKY)
.setReportDelay(2000) // küldemény 2 másodpercenként
.build()
}
// 3. BLE Long Range szkennelés (Coded PHY)
fun longRangeScan(): ScanSettings {
return ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setPhy(ScanSettings.PHY_LE_CODED)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build()
}
// 4. Szkennelés csak 2M PHY-n (BLE 5.0+)
fun highSpeedScan(): ScanSettings {
return ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setPhy(ScanSettings.PHY_LE_2M)
.build()
}
}
A ScanSettingsProvider osztály tipikus konfigurációkat tartalmaz. A lowLatencyScan — UI-szkenneléshez (‘itt és most’ keresés). A lowPowerScan — háttér-figyeléshez küldeménnyel 2 másodpercenként és FIRST_MATCH callbackType-pal (csak az első érzékelésnél aktiválódik). A longRangeScan a PHY_LE_CODED-et (BLE Long Range, akár 1 km) használja. A highSpeedScan — PHY_LE_2M (2 Mbit/s, csak BLE 5.0+ eszközök).
ScanFilter — osztály a BLE-szkennelési eredmények szűrésére. Szűrő nélkül a BluetoothLeScanner az összes BLE-eszközt visszaadja a hatókörben — sűrű BLE-környezetben ez száz csomagot is jelenthet percenként. A ScanFilter leszűkíti az eredményeket a szükséges eszközökre, csökkentve az energiafogyasztást és az alkalmazás terhelését. A szűrők a Bluetooth-verem szintjén kerülnek alkalmazásra — a nem megfelelő csomagok elutasításra kerülnek, mielőtt az alkalmazáshoz érkeznek.
Szűrőtípusok: setServiceUuid — szolgáltatás UUID (kötelezően teljes 128-bites formátum). setDeviceName — eszköznév részsztringje (kis-és nagybetű érzékeny, pontos részsztring-egyezés). setDeviceAddress — pontos MAC-cím. setManufacturerData — gyártói adatok (vállalat ID + maszk). Egy szkenneléshez több szűrő is beállítható — az eszköznek mindegyiknek meg kell felelnie (ÉS logika). VAGY logikához indítson több szkennelést.
// ScanFilter létrehozása különböző forgatókönyvekhez
class ScanFilterFactory {
// 1. Szűrés szolgáltatás UUID alapján (Heart Rate Monitor)
fun byHeartRateService(): ScanFilter {
return ScanFilter.Builder()
.setServiceUuid(
ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB")
)
.build()
}
// 2. Szűrés eszköznév alapján („iBeacon*")
fun byDeviceName(): ScanFilter {
return ScanFilter.Builder()
.setDeviceName("Sensor")
.build()
}
// 3. MAC- (eszközök)
fun byMacAddress(mac: String): ScanFilter {
return ScanFilter.Builder()
.setDeviceAddress(mac)
.build()
}
// 4. Kombinált szűrő (UUID + )
fun combinedFilter(): List<ScanFilter> {
return listOf(
ScanFilter.Builder()
.setServiceUuid(
ParcelUuid.fromString("0000A001-0000-1000-8000-00805F9B34FB")
)
.setDeviceName("MyDevice")
.build()
)
}
// 5. gyártói adatok
fun byManufacturer(companyId: Int, data: ByteArray, mask: ByteArray): ScanFilter {
return ScanFilter.Builder()
.setManufacturerData(companyId, data, mask)
.build()
}
}
A ScanFilterFactory osztály az összes szűrőtípust megmutatja. A byHeartRateService szűri az eszközöket pulzusszolgáltatással (0x180D). A byDeviceName megtalálja azokat az eszközöket, amelyek ‘Sensor’ részt tartalmaznak a névben (az Apple egyedi neveket ajánl a szűréshez). A byMacAddress — egy adott eszköz pontos keresése. A combinedFilter — ÉS szűrő UUID és név alapján. A byManufacturer — szűrő gyártói adatok alapján (például az iBeacon esetében az Apple company ID 0x004C használatos).
ScanCallback — absztrakt osztály a BLE-szkennelési eredmények fogadására. Három metódust tartalmaz: onScanResult — egyedi eredmény (callback típus, ScanResult), onBatchScanResults — eredményküldemény a reportDelay > 0 esetén, onScanFailed — hibakód. Minden metódus az Android fő szálán (main thread) kerül meghívásra. Hosszú feldolgozáshoz az onScanResult-ban használjon korutinokat vagy HandlerThread-et.
ScanResult tartalmazza: BluetoothDevice device (eszköz), int rssi (jelerősség dBm-ben), ScanRecord scanRecord (reklámadatok), long timestampNanos (érzékelés ideje a rendszerindítás óta). A ScanRecord biztosítja: getServiceData() — UUID + egyedi adatok, getManufacturerSpecificData() — gyártói adatok, getAdvertiseFlags() — BLE-zászlók. A callback típus (callbackType) jelzi: CALLBACK_TYPE_ALL_MATCHES — egyezés a szűrővel, CALLBACK_TYPE_FIRST_MATCH — első érzékelés, CALLBACK_TYPE_MATCH_LOST — eszköz elvesztése.
Az onScanFailed hibakódjai: SCAN_FAILED_ALREADY_STARTED (1) — a szkennelés már elindult, SCAN_FAILED_APPLICATION_REGISTRATION_FAILED (2) — az alkalmazás regisztrációja a Bluetooth-veremben sikertelen volt, SCAN_FAILED_INTERNAL_ERROR (3) — a verem belső hibája, SCAN_FAILED_FEATURE_UNSUPPORTED (4) — a BLE-szkennelés nem támogatott az eszközön.
// Teljes szkennelési eredmények és hibakezelés
class ScanResultHandler {
private val results = mutableListOf<ScanResult>()
val scanCallback = object : ScanCallback() {
// 1. Egyedi eredmény
override fun onScanResult(callbackType: Int, result: ScanResult) {
// callbackType: 1 = ALL_MATCHES, 2 = FIRST_MATCH, 4 = MATCH_LOST
if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST) {
onDeviceLost(result)
return
}
// Hozzáadás a listához (deduplikáció cím alapján)
val existingIndex = results.indexOfFirst {
it.device.address == result.device.address
}
if (existingIndex >= 0) {
results[existingIndex] = result // RSSI frissítése
} else {
results.add(result)
}
// Adatok kinyerése a reklámcsomagból
val record = result.scanRecord
val serviceData = record?.serviceData
val manufacturerData = record?.manufacturerSpecificData
print("Found: ${result.device.name ?: "N/A"}, RSSI: ${result.rssi}")
}
// 2. Küldeményes eredmények (reportDelay > 0)
override fun onBatchScanResults(results: MutableList<ScanResult>?) {
results?.let { batch ->
print("Batch: ${batch.size} devices")
}
}
// 3. Szkennelési hiba
override fun onScanFailed(errorCode: Int) {
val error = when (errorCode) {
ScanCallback.SCAN_FAILED_ALREADY_STARTED -> "Already scanning"
ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED -> "Registration failed"
ScanCallback.SCAN_FAILED_INTERNAL_ERROR -> "Internal error"
ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED -> "BLE not supported"
else -> "Unknown error: $errorCode"
}
print("Error: $error")
}
}
private fun onDeviceLost(result: ScanResult) {
results.removeAll { it.device.address == result.device.address }
print("Device lost: ${result.device.address}")
}
}
A ScanResultHandler osztály a BluetoothLeScanner összes callback típusát feldolgozza. Az onScanResult frissíti az eszközök listáját deduplikációval MAC-cím alapján — az RSSI frissül a már megtalált eszközökhöz. A CALLBACK_TYPE_MATCH_LOST az eszköz elvesztését jelzi (eltávolítás a listából). Az onBatchScanResults a küldeményes eredményeket dolgozza fel a reportDelay > 0 esetén. Az onScanFailed a hibakódokat olvasható üzenetekké képezi le — elengedhetetlen a BLE-szkennelés hibakereséséhez.
PendingIntent-szkennelés — a BluetoothLeScanner mechanizmusa a BLE-szkenneléshez, amely még akkor is működik, amikor az alkalmazás a háttérben van (az Android 8+ korlátozásaival). A ScanCallback helyett PendingIntent-et használ, amely Broadcast-et küld a rendszer BroadcastReceiver-jének BLE-eszköz érzékelésekor. Ez lehetővé teszi az alkalmazás számára, hogy értesítéseket kapjon a BLE-eszközökről anélkül, hogy a memóriában lenne (a rendszer folyamatot hoz létre a broadcast fogadásakor).
A háttér-szkennelés korlátozásai: Android 8+ (API 26) rendszeren a háttérszolgáltatások korlátozottak — a PendingIntent-szkennelés megkerüli ezt a korlátozást a BroadcastReceiveren keresztül, amelyet a rendszer elindíthat BLE-esemény érkezésekor. Android 10+ (API 29) rendszeren a háttér BLE-szkennelés tovább korlátozott a gyártók energiatakarékossági irányelvei által (a Xiaomi, Huawei, Samsung blokkolják a háttér BLE-műveleteket). Kritikus BLE-forgatókönyvekhez értesítés szükséges foreground service-szel.
// Háttér BLE-szkennelés PendingIntent segítségével
class BackgroundBLEScanner(private val context: Context) {
private val scanner: BluetoothLeScanner? by lazy {
val adapter = BluetoothAdapter.getDefaultAdapter()
adapter?.bluetoothLeScanner
}
fun startBackgroundScan() {
// PendingIntent létrehozása BroadcastReceiver számára
val intent = Intent(context, BLEBroadcastReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
// Háttér-szkennelés beállításai
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
.setMatchMode(ScanSettings.MATCH_MODE_STICKY)
.build()
// Háttér-szkennelés indítása
scanner?.startScan(
null, // szűrők
settings,
pendingIntent
)
}
fun stopBackgroundScan() {
val intent = Intent(context, BLEBroadcastReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
scanner?.stopScan(pendingIntent)
}
}
// BroadcastReceiver BLE-
class BLEBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// Szkennelési eredmények lekérése
val results = BluetoothLeScanner.getPendingIntentScanResults(intent)
results?.let { scanResults ->
for (result in scanResults) {
// Értesítés küldése a felhasználónak
showNotification(context, result.device.name ?: " ")
}
}
}
private fun showNotification(context: Context, name: String) {
val notification = Notification.Builder(context, "ble_channel")
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle("BLE devices")
.setContentText("Found: $name")
.setAutoCancel(true)
.build()
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE)
as NotificationManager
manager.notify(System.currentTimeMillis().toInt(), notification)
}
}
A BackgroundBLEScanner osztály elindítja a háttér BLE-szkennelést PendingIntent segítségével. A startBackgroundScan létrehoz egy PendingIntent-et, amely BLE-eszköz érzékelésekor Broadcast-et küld a BLEBroadcastReceiver-nek. A BroadcastReceiver kinyeri a ScanResult-ot a getPendingIntentScanResults() segítségével, és értesítést jeleníthet meg vagy adatokat küldhet a szerverre. Ez a megközelítés akkor is működik, ha az alkalmazást a rendszer befejezte — az Android elindítja a BroadcastReceiver-t a broadcast érkezésekor.
Teljes példa BLE-szkennerről Kotlinban, amely BluetoothLeScanner-t használ ScanSettings, ScanFilter és ScanCallback segítségével Heart Rate Monitor eszközök keresésére. A szkenner megjeleníti a megtalált eszközök listáját RSSI és szolgáltatás UUID-kkel, BluetoothGatt-on keresztüli csatlakozási lehetőséggel.
// Teljes BLE-szkenner korutinokkal Kotlinban
class DeviceScanner(private val context: Context) {
private val adapter: BluetoothAdapter? by lazy {
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE)
as BluetoothManager
manager.adapter
}
private val scanner: BluetoothLeScanner? by lazy {
adapter?.bluetoothLeScanner
}
fun startScan(duration: Long = 10000): Flow<ScanResult> = callbackFlow {
// Bluetooth állapotának ellenőrzése
if (adapter?.isEnabled != true) {
close(IllegalStateException("Bluetooth disabled"))
return@callbackFlow
}
// Szkennelés konfigurációja
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build()
val filters = listOf(
ScanFilter.Builder()
.setServiceUuid(ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
.build()
)
val callback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
trySend(result)
}
override fun onScanFailed(errorCode: Int) {
close(BLEException("Scan failed: $errorCode"))
}
}
// Szkennelés indítása
scanner?.startScan(filters, settings, callback)
// Automatikus leállítás időtartam után
delay(duration)
scanner?.stopScan(callback)
close()
}.flowOn(Dispatchers.IO)
fun stop() {
scanner?.stopScan(object : ScanCallback() {})
}
}
class BLEException(message: String) : Exception(message)
A DeviceScanner osztály Kotlin Flow-t (callbackFlow) használ reaktív BLE-szkenneléshez. A szkennelés LOW_LATENCY beállításokkal és Heart Rate Service UUID szűrővel indul. Az eredmények az onScanResult-on keresztül kerülnek a Flow-ba. Automatikus leállítás a megadott időtartam után (alapértelmezés szerint 10 másodperc). A FlowOn(Dispatchers.IO) áthelyezi a BLE-műveleteket a háttérszálba. Ez a megközelítés lehetővé teszi a BLE-szkennelés használatát MVVM architektúrában a viewModelScope.launch és collect segítségével.
Gyakran Ismételt Kérdések
BluetoothLeScanner — Android osztály (API 21+) BLE-szkenneléshez. A BluetoothAdapter.getBluetoothLeScanner() segítségével szerezhető meg. Három szkennelési módot (LOW_POWER, BALANCED, LOW_LATENCY), szűrést UUID, név és MAC-cím alapján, küldeményes eredményeket és PendingIntent-et támogat a háttér-szkenneléshez. Felváltja az elavult BluetoothAdapter.startLeScan() metódust.
SCAN_MODE_LOW_POWER — háttérmód 5–10 másodperces érzékelési késleltetéssel, minimális energiafogyasztás. SCAN_MODE_LOW_LATENCY — aktív mód körülbelül 100 ms késleltetéssel, maximális energiafogyasztás. SCAN_MODE_BALANCED — kompromisszum (~2 másodperc késleltetés). UI-szkenneléshez használja a LOW_LATENCY-t, háttér-figyeléshez a LOW_POWER-t PendingIntent-tel.
Okok: Bluetooth kikapcsolva (ellenőrizze az adapter.isEnabled-et), nincsenek engedélyek (BLUETOOTH_SCAN API 31+, ACCESS_FINE_LOCATION API 23–30 esetén), scanner = null (adapter nem elérhető), eszköz hatókörön kívül vagy helytelen szűrő használata. Ellenőrizze az onScanFailed-et is — a hibakód jelzi az okot: SCAN_FAILED_ALREADY_STARTED (1) vagy SCAN_FAILED_APPLICATION_REGISTRATION_FAILED (2).
Használja a PendingIntent változatát a startScan()-nek — adja át a PendingIntent-et a ScanCallback helyett. BLE-eszköz érzékelésekor az Android Broadcast-et küld a BroadcastReceiver-nek, amelyet a rendszer elindíthat, még ha az alkalmazás a háttérben van. Android 8+ esetén adja hozzá a BroadcastReceiver-t a manifesztumhoz. Android 10+ esetén vegye figyelembe a gyártók energiatakarékossági korlátozásait.
A BluetoothLeScanner nem rendelkezik korláttal az érzékelhető eszközök számára — a korlát a környezet BLE-telítettségétől függ. Egy irodában 20–50 aktív BLE-eszköz lehet, egy bevásárlóközpontban százak. Szűréshez használja a ScanFilter-t (UUID, név alapján). Szűrő nélkül dolgozza fel az eredményeket aszinkron módon — az onScanResult akár tízszer is meghívható másodpercenként.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is