BluetoothLeScanner — Bluetooth Low Energy cihazlarını skan etmək üçün Android sinifi, API 21-dən (Android 5.0) mövcuddur. BluetoothLeScanner, BluetoothAdapter-də köhnəlmiş startLeScan metodunu əvəz edərək, çevik API təmin edir: skan parametrləri (ScanSettings), filtrləmə (ScanFilter) və fon rejimi dəstəyi (PendingIntent). Nümunə BluetoothAdapter.getBluetoothLeScanner() vasitəsilə əldə edilir. Android Developers, 2026-ya görə, BluetoothLeScanner üç enerji rejimini dəstəkləyir və BLE reklam paketlərini xidmət UUID-si, cihaz adı və ya MAC ünvanı üzrə filtrləmə ilə skan etməyə imkan verir.
Başlıca
BluetoothLeScanner — Android-də BLE skan etməsini idarə etmək üçün sistem sinifi. Sadə LeScanCallback qəbul edən BluetoothAdapter.startLeScan()-dən fərqli olaraq, BluetoothLeScanner parametrlər, filtrlər və genişləndirilmiş xəta idarəetməsi ilə obyekt yönümlü API təmin edir. Sinif API 21-də (Android 5.0) BLE 4.2 dəstəyi ilə birlikdə ortaya çıxdı və bütün müasir Android versiyalarında BLE skan etmənin əsas üsulu olaraq qalır.
BluetoothLeScanner nümunəsinin əldə edilməsi BluetoothAdapter.getBluetoothLeScanner() vasitəsilə həyata keçirilir. Bluetooth adapteri mövcud deyilsə (Bluetooth söndürülüb və ya cihaz BLE-ni dəstəkləmir), metod null qaytarır. Əldə etməzdən əvvəl BluetoothAdapter.isEnabled() və PackageManager vasitƏlə FEATURE_BLUETOOTH_LE-nin mövcudluğunu yoxlayın. Skaner əldə edildikdən sonra istənilən thread-da skan etməyə başlaya bilərsiniz — Android özü BLE əməliyyatlarını Bluetooth stackinin daxili thread-ında planlaşdırır.
// BluetoothLeScanner-ın əldə edilməsi
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 mövcudluğunu yoxlayın
if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
return false
}
// Bluetooth-un aktiv olduğunu yoxlayın
if (adapter?.isEnabled != true) {
return false
}
// Skaneri əldə edin
scanner = adapter?.bluetoothLeScanner
return scanner != null
}
// Skanerin mövcudluğunu yoxlayın
val isAvailable: Boolean
get() = scanner != null
// Filtrlər olmadan əsas skanı başlatın
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}")
}
}
BLEScannerManager sinifi BluetoothLeScanner-ın təhlükəsiz əldə edilməsi və ilkinləşdirilməsini nümayiş etdirir. initScanner BLE-nin mövcudluğunu hasSystemFeature, Bluetooth-un aktivliyini və skanerin uğurla əldə edilməsini yoxlayır. startBasicScan parametrlər və filtrlər olmadan skan etməyə başlayır — əhatə dairəsindəki bütün BLE cihazlarını aşkarlayır. handleResult ScanResult-u təhlil edir: BluetoothDevice (ad, ünvan), RSSI (siqnal səviyyəsi), scanRecord (reklam məlumatları).
ScanSettings — BLE skan etməsinin konfiqurasiyası üçün sinif. Əsas parametr — enerji sərfiyyatı ilə aşkarlama gecikməsi arasında kompromis müyyən edən skan rejimi (scanMode)-dir. ScanSettings.Builder aşağıdakıları konfiqurasiya etməyə imkan verir: scanMode, callbackType (CALLBACK_TYPE_ALL_MATCHES, CALLBACK_TYPE_FIRST_MATCH, CALLBACK_TYPE_MATCH_LOST), matchMode (MATCH_MODE_AGGRESSIVE, MATCH_MODE_STICKY), reportDelay (toplu göndərmə gecikməsi) və phy (PHY_LE_1M, PHY_LE_2M, PHY_LE_CODED).
Üç skan rejimi: SCAN_MODE_LOW_POWER (0) — aşağı enerji sərfiyyatı ilə fon skan etmə, aşkarlama gecikməsi bir neçə saniyə. SCAN_MODE_BALANCED (1) — əksər ssenarilər üçün balanslaşdırılmış rejim. SCAN_MODE_LOW_LATENCY (2) — minimal aşkarlama gecikməsi (təxminən 100 ms), maksimum enerji sərfiyyatı. Aktiv cihaz axtarışı üçün LOW_LATENCY, fon monitorinqi üçün LOW_POWER istifadə edin.
reportDelay — nəticələrin qrup şəklində göndərilməsindən əvvəl millisaniyələrlə gecikmə. reportDelay = 0 olarsa, nəticələr aşkarlanandan dərhal sonra göndərilir. > 0 olarsa, Android nəticələri toplayır və onBatchScanResults vasitəsilə toplu göndərir. Toplu göndərmə callback çağırışlarının sayını azaldır və enerji sərfiyyatını aşağı salır, aşağı prioritetli fon skan etməsi üçün uyğundur.
// Müxtəlif ssenarilər üçün ScanSettings konfiqurasiyası
class ScanSettingsProvider {
// 1. Sürətli skan (aktiv axtarış)
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. Enerji səmərəli skan (fon monitorinqi)
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) // hər 2 saniyədən bir toplu
.build()
}
// 3. BLE Long Range skan (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. Yalnız 2M PHY-də skan (BLE 5.0+)
fun highSpeedScan(): ScanSettings {
return ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setPhy(ScanSettings.PHY_LE_2M)
.build()
}
}
ScanSettingsProvider sinifi tipik konfiqurasiyaları ehtiva edir. lowLatencyScan — UI skan etməsi üçün („burada və indi” axtarışı). lowPowerScan — hər 2 saniyədən bir toplu və FIRST_MATCH callbackTipi ilə fon monitorinqi üçün (yalnız ilk aşkarlamada işləyir). longRangeScan PHY_LE_CODED (BLE Long Range, 1 km-ə qədər) istifadə edir. highSpeedScan — PHY_LE_2M (2 Mbit/s, yalnız BLE 5.0+ cihazları).
ScanFilter — BLE skan etmə nəticələrinin filtrlənməsi üçün sinif. Filtr olmadan BluetoothLeScanner əhatə dairəsindəki bütün BLE cihazlarını qaytarır — sıx BLE mühitində dəqiqədə yüzlərlə paket. ScanFilter nəticələri lazım cihazlara qədər daraldır, enerji sərfiyyatını və tətbiq yükünü azaldır. Filtrlər Bluetooth stack səviyyəsində tətbiq olunur — uyğun olmayan paketlər tətbiqə çatdırılmazdan əvvəl rədd edilir.
Filtr növləri: setServiceUuid — xidmət UUID-si (mütləq tam 128-bit format). setDeviceName — cihaz adının alt sətri (böyük/kiçik hərf həssaslığı ilə, dəqiq alt sətr uyğunluğu). setDeviceAddress — dəqiq MAC ünvanı. setManufacturerData — istehsalçı məlumatları (şirkət ID-si + maska). Bir skan üçün bir neçə filtr qurmaq olar — cihaz hamısına uyğun olmalıdır (AND məntiqi). OR məntiqi üçün bir neçə skan işə salın.
// Müxtəlif ssenarilər üçün ScanFilter yaradılması
class ScanFilterFactory {
// 1. Xidmət UUID-si üzrə filtr (Heart Rate Monitor)
fun byHeartRateService(): ScanFilter {
return ScanFilter.Builder()
.setServiceUuid(
ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB")
)
.build()
}
// 2. Cihaz adı üzrə filtr („iBeacon*")
fun byDeviceName(): ScanFilter {
return ScanFilter.Builder()
.setDeviceName("Sensor")
.build()
}
// 3. MAC- (cihazlar)
fun byMacAddress(mac: String): ScanFilter {
return ScanFilter.Builder()
.setDeviceAddress(mac)
.build()
}
// 4. Birləşmiş filtr (UUID + )
fun combinedFilter(): List<ScanFilter> {
return listOf(
ScanFilter.Builder()
.setServiceUuid(
ParcelUuid.fromString("0000A001-0000-1000-8000-00805F9B34FB")
)
.setDeviceName("MyDevice")
.build()
)
}
// 5. istehsalçı məlumatları
fun byManufacturer(companyId: Int, data: ByteArray, mask: ByteArray): ScanFilter {
return ScanFilter.Builder()
.setManufacturerData(companyId, data, mask)
.build()
}
}
ScanFilterFactory sinifi bütün filtr növlərini göstərir. byHeartRateService 0x180D nəbz xidməti olan cihazları filtrəyir. byDeviceName adında „Sensor” olan cihazları tapır (Apple filtrləmə üçün unikal adlar tövsiyə edir). byMacAddress — konkret cihazın dəqiq axtarışı. combinedFilter — UUID və ad üzrə AND filtri. byManufacturer — istehsalçı məlumatları üzrə filtr (məsələn, iBeacon üçün Apple company ID 0x004C istifadə olunur).
ScanCallback — BLE skan etmə nəticələrini əldə etmək üçün abstrakt sinif. Üç metodu ehtiva edir: onScanResult — tək nəticə (callback tipi, ScanResult), onBatchScanResults — reportDelay > 0 üçün nəticələr toplusu, onScanFailed — xəta kodu. Bütün metodlar Android əsas thread-ında (main thread) çağırılır. onScanResult-da uzunmüddətli emal üçün korutinlər və ya HandlerThread istifadə edin.
ScanResult ehtiva edir: BluetoothDevice device (cihaz), int rssi (dBm ilə siqnal səviyyəsi), ScanRecord scanRecord (reklam məlumatları), long timestampNanos (sistemin yüklənməsindən etibarən aşkarlama vaxtı). ScanRecord təmin edir: getServiceData() — UUID + fərdi məlumatlar, getManufacturerSpecificData() — istehsalçı məlumatları, getAdvertiseFlags() — BLE bayraqları. Callback tipi (callbackType) göstərir: CALLBACK_TYPE_ALL_MATCHES — filtrlə uyğunluq, CALLBACK_TYPE_FIRST_MATCH — ilk aşkarlama, CALLBACK_TYPE_MATCH_LOST — cihazın itirilməsi.
onScanFailed xəta kodları: SCAN_FAILED_ALREADY_STARTED (1) — skan artıq işləyir, SCAN_FAILED_APPLICATION_REGISTRATION_FAILED (2) — tətbiqin Bluetooth stack-də qeydiyyatı uğursuz oldu, SCAN_FAILED_INTERNAL_ERROR (3) — stack-in daxili xətası, SCAN_FAILED_FEATURE_UNSUPPORTED (4) — BLE skan etmə cihazda dəstəklənmir.
// Tam skan nəticələri və xəta idarəetməsi
class ScanResultHandler {
private val results = mutableListOf<ScanResult>()
val scanCallback = object : ScanCallback() {
// 1. Tək nəticə
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
}
// Siyahıya əlavə et (ünvan üzrə deduplikasiya)
val existingIndex = results.indexOfFirst {
it.device.address == result.device.address
}
if (existingIndex >= 0) {
results[existingIndex] = result // RSSI-ni yenilə
} else {
results.add(result)
}
// Reklam paketindən məlumatları çıxarın
val record = result.scanRecord
val serviceData = record?.serviceData
val manufacturerData = record?.manufacturerSpecificData
print("Found: ${result.device.name ?: "N/A"}, RSSI: ${result.rssi}")
}
// 2. Toplu nəticələr (reportDelay > 0)
override fun onBatchScanResults(results: MutableList<ScanResult>?) {
results?.let { batch ->
print("Batch: ${batch.size} devices")
}
}
// 3. Skan xətası
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}")
}
}
ScanResultHandler sinifi BluetoothLeScanner-ın bütün callback növlərini emal edir. onScanResult cihaz siyahısını MAC ünvanı üzrə deduplikasiya ilə yeniləyir — artıq tapılmış cihazlar üçün RSSI yenilənir. CALLBACK_TYPE_MATCH_LOST cihazın itirilməsini bildirir (siyahıdan silinmə). onBatchScanResults reportDelay > 0 üçün toplu nəticələri emal edir. onScanFailed xəta kodlarını oxunaqlı mesajlara çevirir — BLE skan etməsinin debug edilməsi üçün kritikdir.
PendingIntent skan etməsi — BluetoothLeScanner-ın tətbiq fonda olsa belə işləyən BLE skan mexanizmi (Android 8+ məhdudiyyətləri ilə). ScanCallback əvəzinə PendingIntent istifadə olunur ki, bu da BLE cihazı aşkarlananda sistem BroadcastReceiver-ına Broadcast göndərir. Bu, tətbiqin yaddaşda olmadan BLE cihazları haqqında bildirişlər almasına imkan verir (sistem broadcast alındıqda proses yaradır).
Fon skan etmə məhdudiyyətləri: Android 8+-də (API 26) fon xidmətləri məhduddur — PendingIntent skan etməsi bu məhdudiyyəti BroadcastReceiver vasitəsilə keçir. Android 10+-da (API 29) fon BLE skan etməsi əlavə olaraq istehsalçıların enerji qənaət siyasətləri ilə məhdudlaşdırılır (Xiaomi, Huawei, Samsung fon BLE əməliyyatlarını bloklayır). Kritik BLE ssenariləri üçün foreground service ilə bildiriş tələb olunur.
// PendingIntent vasitəsilə fon BLE skan etmə
class BackgroundBLEScanner(private val context: Context) {
private val scanner: BluetoothLeScanner? by lazy {
val adapter = BluetoothAdapter.getDefaultAdapter()
adapter?.bluetoothLeScanner
}
fun startBackgroundScan() {
// BroadcastReceiver üçün PendingIntent yaradın
val intent = Intent(context, BLEBroadcastReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
// Fon skan parametrləri
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
.setMatchMode(ScanSettings.MATCH_MODE_STICKY)
.build()
// Fon skanı başlatın
scanner?.startScan(
null, // filtrlər
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) {
// Skan nəticələrini əldə edin
val results = BluetoothLeScanner.getPendingIntentScanResults(intent)
results?.let { scanResults ->
for (result in scanResults) {
// İstifadəçiyə bildiriş göndərin
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)
}
}
BackgroundBLEScanner sinifi PendingIntent vasitəsilə fon BLE skan etməsini işə salır. startBackgroundScan PendingIntent yaradır ki, BLE cihazı aşkarlananda BLEBroadcastReceiver-ına Broadcast göndərir. BroadcastReceiver getPendingIntentScanResults() vasitəsilə ScanResult çıxarır və bildiriş göstərə və ya məlumatları serverə göndərə bilər. Bu yanaşma tətbiq sistem tərəfindən bağlansa belə işləyir — Android broadcast alındıqda BroadcastReceiver-ı işə salır.
Tam nümunə — Heart Rate Monitor cihazlarını axtarmaq üçün BluetoothLeScanner, ScanSettings, ScanFilter və ScanCallback istifadə edən Kotlin BLE skaneri. Skaner tapılmış cihazların siyahısını RSSI və xidmət UUID-ləri ilə göstərir, BluetoothGatt vasitəsilə qoşulma imkanı ilə.
// Kotlin-də korutinlərlə tam BLE skaneri
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 vəziyyətini yoxlayın
if (adapter?.isEnabled != true) {
close(IllegalStateException("Bluetooth disabled"))
return@callbackFlow
}
// Skan konfiqurasiyası
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"))
}
}
// Skan etməyə başlayın
scanner?.startScan(filters, settings, callback)
// Müddətdən sonra avtomatik dayanma
delay(duration)
scanner?.stopScan(callback)
close()
}.flowOn(Dispatchers.IO)
fun stop() {
scanner?.stopScan(object : ScanCallback() {})
}
}
class BLEException(message: String) : Exception(message)
DeviceScanner sinifi reaktiv BLE skan etməsi üçün Kotlin Flow (callbackFlow) istifadə edir. Skan LOW_LATENCY parametrləri və Heart Rate Service UUID filtri ilə işə salınır. Nəticələr onScanResult vasitəsilə Flow-a emit edilir. Verilmiş müddətdən sonra avtomatik dayanma (standart olaraq 10 saniyə). FlowOn(Dispatchers.IO) BLE əməliyyatlarını fon thread-ına çıxarır. Bu yanaşma MVVM arxitekturasında viewModelScope.launch və collect vasitəsilə BLE skan etməsindən istifadə etməyə imkan verir.
Tez-tez verilən suallar
BluetoothLeScanner — BLE skan etməsi üçün Android sinifi (API 21+). BluetoothAdapter.getBluetoothLeScanner() vasitəsilə əldə edilir. Üç skan rejimini (LOW_POWER, BALANCED, LOW_LATENCY), UUID, ad və MAC ünvanı üzrə filtrləməni, toplu nəticələri və fon skan etməsi üçün PendingIntent-i dəstəkləyir. Köhnəlmiş BluetoothAdapter.startLeScan() metodunu əvəz edir.
SCAN_MODE_LOW_POWER — aşkarlama gecikməsi 5–10 saniyə olan fon rejimi, minimal enerji sərfiyyatı. SCAN_MODE_LOW_LATENCY — təxminən 100 ms gecikməli aktiv rejim, maksimum enerji sərfiyyatı. SCAN_MODE_BALANCED — kompromis (~2 saniyə gecikmə). UI skan etməsi üçün LOW_LATENCY, fon monitorinqi üçün LOW_POWER PendingIntent ilə istifadə edin.
Səbəblər: Bluetooth söndürülüb (adapter.isEnabled yoxlayın), icazələr alınmayıb (API 31+ üçün BLUETOOTH_SCAN, API 23–30 üçün ACCESS_FINE_LOCATION), scanner = null (adapter mövcud deyil), cihaz əhatə dairəsində deyil və ya yanlış filtr istifadə olunur. Həmçinin onScanFailed yoxlayın — xəta kodu səbəbi göstərəcək: SCAN_FAILED_ALREADY_STARTED (1) və ya SCAN_FAILED_APPLICATION_REGISTRATION_FAILED (2).
PendingIntent versiyası startScan() istifadə edin — ScanCallback əvəzinə PendingIntent ötürün. BLE cihazı aşkarlananda Android BroadcastReceiver-ına Broadcast göndərir, bu da tətbiq fonda olsa belə sistem tərəfindən işə salına bilər. Android 8+ üçün BroadcastReceiver-i manifest-ə əlavə edin. Android 10+-da istehsalçıların enerji qənaət məhdudiyyətlərini nəzərə alın.
BluetoothLeScanner aşkarlanan cihazların sayında məhdudiyyət qoymur — məhdudiyyət mühitin BLE doymasından asılıdır. Ofisdə 20–50 aktiv BLE cihazı, ticarət mərkəzində isə yüzlərləsi ola bilər. Filtrləmə üçün ScanFilter (UUID, ad üzrə) istifadə edin. Filtrləmə olmadan nəticələri asinxron emal edin — onScanResult saniyədə dəfələrlə çağırıla bilər.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun