BluetoothLeScanner — τι είναι, μέθοδοι και σάρωση BLE στο Android

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-07-16 Χρόνος ανάγνωσης: 10 λεπ

BluetoothLeScanner — η κλάση Android για σάρωση συσκευών Bluetooth Low Energy, διαθέσιμη από API 21 (Android 5.0). Το BluetoothLeScanner αντικατέστησε την παρωχημένη μέθοδο startLeScan στο BluetoothAdapter, παρέχοντας ευέλικτο API με ρύθμιση σάρωσης (ScanSettings), φιλτράρισμα (ScanFilter) και υποστήριξη λειτουργίας υποβάθρου (PendingIntent). Το στέλεχος λαμβάνεται μέσω BluetoothAdapter.getBluetoothLeScanner(). Σύμφωνα με Android Developers, 2026, το BluetoothLeScanner υποστηρίζει τρεις λειτουργίες κατανάλωσης ενέργειας και επιτρέπει τη σάρωση διαφημιστικών πακέτων BLE με φιλτράρισμα βάσει UUID υπηρεσίας, ονόματος συσκευής ή MAC διεύθυνσης.

Βασικά σημεία

  • BluetoothLeScanner — σύγχρονο API Android (API 21+) για σάρωση BLE, αντικατάσταση του παρωχημένου startLeScan
  • ScanSettings — ρύθμιση λειτουργίας σάρωσης: LOW_POWER, BALANCED, LOW_LATENCY και τύπος callback
  • ScanFilter — φιλτράρισμα αποτελεσμάτων βάσει UUID υπηρεσίας, ονόματος συσκευής, MAC διεύθυνσης, δεδομένων κατασκευαστή
  • ScanCallback — callback αποτελεσμάτων onScanResult, onBatchScanResults και onScanFailed με κωδικούς σφάλματος
  • PendingIntent — σάρωση υποβάθρου μέσω BroadcastReceiver, ακόμη και όταν η εφαρμογή είναι στο παρασκήνιο

Τι είναι το BluetoothLeScanner: ουσία και λήψη στελέχους

BluetoothLeScanner — η κλάση συστήματος για διαχείριση σάρωσης BLE στο Android. Σε αντίθεση με το BluetoothAdapter.startLeScan() που δέχεται ένα απλό callback LeScanCallback, το BluetoothLeScanner παρέχει αντικειμενοστραφές API με ρυθμίσεις, φίλτρα και εκτεταμένη διαχείριση σφαλμάτων. Η κλάση εμφανίστηκε στο API 21 (Android 5.0) μαζί με υποστήριξη BLE 4.2 και παραμένει η κύρια μέθοδος σάρωσης BLE σε όλες τις σύγχρονες εκδόσεις Android.

Η λήψη στελέχους BluetoothLeScanner γίνεται μέσω BluetoothAdapter.getBluetoothLeScanner(). Η μέθοδος επιστρέφει null εάν ο προσαρμογέας Bluetooth δεν είναι διαθέσιμος (Bluetooth απενεργοποιημένο ή η συσκευή δεν υποστηρίζει BLE). Πριν από τη λήψη, ελέγξτε το BluetoothAdapter.isEnabled() και την παρουσία FEATURE_BLUETOOTH_LE μέσω PackageManager. Μετά τη λήψη του σαρωτή, μπορείτε να ξεκινήσετε σάρωση σε οποιοδήποτε νήμα — το Android προγραμματίζει μόνο του τις λειτουργίες BLE στο εσωτερικό νήμα της στοίβας Bluetooth.

kotlin
// Λήψη BluetoothLeScanner
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
        if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            return false
        }

        // Ελέγξτε Bluetooth ενεργοποιημένο
        if (adapter?.isEnabled != true) {
            return false
        }

        // Λήψη σαρωτή
        scanner = adapter?.bluetoothLeScanner
        return scanner != null
    }

    // Ελέγξτε διαθεσιμότητα σαρωτή
    val isAvailable: Boolean
        get() = scanner != null

    // Έναρξη βασικής σάρωσης χωρίς φίλτρα
    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 δείχνει την ασφαλή λήψη και αρχικοποίηση του BluetoothLeScanner. Το initScanner ελέγχει την παρουσία BLE μέσω hasSystemFeature, το ενεργοποιημένο Bluetooth και την επιτυχή λήψη του σαρωτή. Το startBasicScan ξεκινά σάρωση χωρίς ρυθμίσεις και φίλτρα — ανιχνεύει όλες τις συσκευές BLE στην εμβέλεια. Το handleResult αναλύει το ScanResult: BluetoothDevice (όνομα, διεύθυνση), RSSI (επίπεδο σήματος), scanRecord (διαφημιστικά δεδομένα).

ScanSettings: λειτουργίες σάρωσης και τύπος callback

ScanSettings — κλάση για διαμόρφωση σάρωσης BLE. Η κύρια παράμετρος είναι η λειτουργία σάρωσης (scanMode), που καθορίζει τη συμβιβαστική λύση μεταξύ κατανάλωσης ενέργειας και καθυστέρησης ανίχνευσης. Το ScanSettings.Builder επιτρέπει τη διαμόρφωση: scanMode, callbackType (CALLBACK_TYPE_ALL_MATCHES, CALLBACK_TYPE_FIRST_MATCH, CALLBACK_TYPE_MATCH_LOST), matchMode (MATCH_MODE_AGGRESSIVE, MATCH_MODE_STICKY), reportDelay (καθυστέρηση μαζικής αποστολής) και phy (PHY_LE_1M, PHY_LE_2M, PHY_LE_CODED).

Τρεις λειτουργίες σάρωσης: SCAN_MODE_LOW_POWER (0) — σάρωση υποβάθρου με χαμηλή κατανάλωση ενέργειας, καθυστέρηση ανίχνευσης αρκετά δευτερόλεπτα. SCAN_MODE_BALANCED (1) — ισορροπημένη λειτουργία για τα περισσότερα σενάρια. SCAN_MODE_LOW_LATENCY (2) — ελάχιστη καθυστέρηση ανίχνευσης (περίπου 100 ms), μέγιστη κατανάλωση ενέργειας. Για ενεργή αναζήτηση συσκευών χρησιμοποιήστε LOW_LATENCY, για παρακολούθηση υποβάθρου — LOW_POWER.

reportDelay — καθυστέρηση σε χιλιοστά του δευτερολέπτου πριν από τη μαζική αποστολή αποτελεσμάτων. Εάν reportDelay = 0, τα αποτελέσματα αποστέλλονται αμέσως μετά την ανίχνευση. Εάν > 0, το Android συσσωρεύει τα αποτελέσματα και στέλνει παρτίδα μέσω onBatchScanResults. Η μαζική αποστολή μειώνει τον αριθμό κλήσεων callback και μειώνει την κατανάλωση ενέργειας, κατάλληλη για σάρωση υποβάθρου χαμηλής προτεραιότητας.

kotlin
// Διαμόρφωση ScanSettings για διαφορετικά σενάρια
class ScanSettingsProvider {

    // 1. Γρήγορη σάρωση (ενεργή αναζήτηση)
    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. Ενεργειακά αποδοτική σάρωση (παρακολούθηση υποβάθρου)
    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)  // παρτίδα κάθε 2 δευτερόλεπτα
            .build()
    }

    // 3. Σάρωση BLE Long Range (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. Σάρωση μόνο σε 2M PHY (BLE 5.0+)
    fun highSpeedScan(): ScanSettings {
        return ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setPhy(ScanSettings.PHY_LE_2M)
            .build()
    }
}

Η κλάση ScanSettingsProvider περιέχει τυπικές διαμορφώσεις. lowLatencyScan — για σάρωση UI (αναζήτηση "εδώ και τώρα"). lowPowerScan — για παρακολούθηση υποβάθρου με παρτίδα κάθε 2 δευτερόλεπτα και callbackType FIRST_MATCH (ενεργοποιείται μόνο στην πρώτη ανίχνευση). longRangeScan χρησιμοποιεί PHY_LE_CODED (BLE Long Range, έως 1 km). highSpeedScan — PHY_LE_2M (2 Mbit/s, μόνο συσκευές BLE 5.0+).

ScanFilter: φιλτράρισμα συσκευών BLE βάσει UUID και ονόματος

ScanFilter — κλάση για φιλτράρισμα αποτελεσμάτων σάρωσης BLE. Χωρίς φίλτρο, το BluetoothLeScanner επιστρέφει όλες τις συσκευές BLE στην εμβέλεια — σε πυκνό περιβάλλον BLE, αυτό σημαίνει εκατοντάδες πακέτα ανά λεπτό. Το ScanFilter περιορίζει τα αποτελέσματα στις απαραίτητες συσκευές, μειώνοντας την κατανάλωση ενέργειας και το φορτίο της εφαρμογής. Τα φίλτρα εφαρμόζονται σε επίπεδο στοίβας Bluetooth — τα ακατάλληλα πακέτα απορρίπτονται πριν από την παράδοση στην εφαρμογή.

Τύποι φίλτρων: setServiceUuid — UUID υπηρεσίας (υποχρεωτικά πλήρης μορφή 128-bit). setDeviceName — υποσυμβολοσειρά ονόματος συσκευής (διάκριση πεζών-κεφαλαίων, ακριβής αντιστοίχιση υποσυμβολοσειράς). setDeviceAddress — ακριβής MAC διεύθυνση. setManufacturerData — δεδομένα κατασκευαστή (ID εταιρείας + μάσκα). Για μία σάρωση μπορούν να οριστούν πολλά φίλτρα — η συσκευή πρέπει να ταιριάζει με όλα (AND λογική). Για OR λογική, εκτελέστε πολλές σαρώσεις.

kotlin
// Δημιουργία ScanFilter για διαφορετικά σενάρια
class ScanFilterFactory {

    // 1. Φιλτράρισμα βάσει UUID υπηρεσίας (Heart Rate Monitor)
    fun byHeartRateService(): ScanFilter {
        return ScanFilter.Builder()
            .setServiceUuid(
                ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB")
            )
            .build()
    }

    // 2. Φιλτράρισμα βάσει ονόματος συσκευής ("iBeacon*")
    fun byDeviceName(): ScanFilter {
        return ScanFilter.Builder()
            .setDeviceName("Sensor")
            .build()
    }

    // 3.   MAC- (συσκευές)
    fun byMacAddress(mac: String): ScanFilter {
        return ScanFilter.Builder()
            .setDeviceAddress(mac)
            .build()
    }

    // 4. Συνδυασμένο φίλτρο (UUID + )
    fun combinedFilter(): List<ScanFilter> {
        return listOf(
            ScanFilter.Builder()
                .setServiceUuid(
                    ParcelUuid.fromString("0000A001-0000-1000-8000-00805F9B34FB")
                )
                .setDeviceName("MyDevice")
                .build()
        )
    }

    // 5.   δεδομένα κατασκευαστή
    fun byManufacturer(companyId: Int, data: ByteArray, mask: ByteArray): ScanFilter {
        return ScanFilter.Builder()
            .setManufacturerData(companyId, data, mask)
            .build()
    }
}

Η κλάση ScanFilterFactory δείχνει όλους τους τύπους φίλτρων. byHeartRateService φιλτράρει συσκευές με υπηρεσία παλμού 0x180D. byDeviceName βρίσκει συσκευές που περιέχουν "Sensor" στο όνομα (η Apple συνιστά μοναδικά ονόματα για φιλτράρισμα). byMacAddress — ακριβής αναζήτηση συγκεκριμένης συσκευής. combinedFilter — AND φίλτρο βάσει UUID και ονόματος. byManufacturer — φίλτρο βάσει δεδομένων κατασκευαστή (π.χ. για iBeacon χρησιμοποιείται company ID Apple 0x004C).

ScanCallback: επεξεργασία αποτελεσμάτων και σφαλμάτων σάρωσης

ScanCallback — αφηρημένη κλάση για λήψη αποτελεσμάτων σάρωσης BLE. Περιέχει τρεις μεθόδους: onScanResult — μεμονωμένο αποτέλεσμα (τύπος callback, ScanResult), onBatchScanResults — παρτίδα αποτελεσμάτων για reportDelay > 0, onScanFailed — κωδικός σφάλματος. Όλες οι μέθοδοι καλούνται στο κύριο νήμα Android (main thread). Για μακροχρόνια επεξεργασία στο onScanResult, χρησιμοποιήστε coroutines ή HandlerThread.

ScanResult περιέχει: BluetoothDevice device (συσκευή), int rssi (επίπεδο σήματος σε dBm), ScanRecord scanRecord (διαφημιστικά δεδομένα), long timestampNanos (χρόνος ανίχνευσης από την εκκίνηση συστήματος). Το ScanRecord παρέχει: getServiceData() — UUID + προσαρμοσμένα δεδομένα, getManufacturerSpecificData() — δεδομένα κατασκευαστή, getAdvertiseFlags() — σημαίες BLE. Ο τύπος callback (callbackType) υποδεικνύει: CALLBACK_TYPE_ALL_MATCHES — αντιστοίχιση με φίλτρο, CALLBACK_TYPE_FIRST_MATCH — πρώτη ανίχνευση, CALLBACK_TYPE_MATCH_LOST — απώλεια συσκευής.

Κωδικοί σφάλματος onScanFailed: SCAN_FAILED_ALREADY_STARTED (1) — η σάρωση έχει ήδη ξεκινήσει, SCAN_FAILED_APPLICATION_REGISTRATION_FAILED (2) — η εγγραφή της εφαρμογής στη στοίβα Bluetooth απέτυχε, SCAN_FAILED_INTERNAL_ERROR (3) — εσωτερικό σφάλμα στοίβας, SCAN_FAILED_FEATURE_UNSUPPORTED (4) — η σάρωση BLE δεν υποστηρίζεται στη συσκευή.

kotlin
// Πλήρη αποτελέσματα σάρωσης και διαχείριση σφαλμάτων
class ScanResultHandler {

    private val results = mutableListOf<ScanResult>()

    val scanCallback = object : ScanCallback() {

        // 1. Μεμονωμένο αποτέλεσμα
        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
            }

            // Προσθήκη στη λίστα (αφαίρεση διπλότυπων βάσει διεύθυνσης)
            val existingIndex = results.indexOfFirst {
                it.device.address == result.device.address
            }
            if (existingIndex >= 0) {
                results[existingIndex] = result  // ενημέρωση RSSI
            } else {
                results.add(result)
            }

            // Εξαγωγή δεδομένων από διαφημιστικό πακέτο
            val record = result.scanRecord
            val serviceData = record?.serviceData
            val manufacturerData = record?.manufacturerSpecificData

            print("Found: ${result.device.name ?: "N/A"}, RSSI: ${result.rssi}")
        }

        // 2. Αποτελέσματα παρτίδας (reportDelay > 0)
        override fun onBatchScanResults(results: MutableList<ScanResult>?) {
            results?.let { batch ->
                print("Batch: ${batch.size} devices")
            }
        }

        // 3. Σφάλμα σάρωσης
        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 επεξεργάζεται όλους τους τύπους callback του BluetoothLeScanner. Το onScanResult ενημερώνει τη λίστα συσκευών με αφαίρεση διπλότυπων βάσει MAC διεύθυνσης — το RSSI ενημερώνεται για ήδη ευρεθείσες συσκευές. Το CALLBACK_TYPE_MATCH_LOST σηματοδοτεί απώλεια συσκευής (αφαίρεση από λίστα). Το onBatchScanResults επεξεργάζεται αποτελέσματα παρτίδας για reportDelay > 0. Το onScanFailed αντιστοιχίζει κωδικούς σφάλματος σε αναγνώσιμα μηνύματα — κρίσιμο για αποσφαλμάτωση σάρωσης BLE.

PendingIntent: σάρωση BLE υποβάθρου μέσω BroadcastReceiver

Σάρωση PendingIntent — μηχανισμός BluetoothLeScanner για σάρωση BLE που λειτουργεί ακόμη και όταν η εφαρμογή είναι στο παρασκήνιο (με περιορισμούς Android 8+). Αντί για ScanCallback, χρησιμοποιείται PendingIntent που στέλνει Broadcast στον BroadcastReceiver συστήματος κατά την ανίχνευση συσκευής BLE. Αυτό επιτρέπει στην εφαρμογή να λαμβάνει ειδοποιήσεις για συσκευές BLE χωρίς να βρίσκεται στη μνήμη (το σύστημα δημιουργεί διεργασία κατά τη λήψη broadcast).

Περιορισμοί σάρωσης υποβάθρου: Σε Android 8+ (API 26) οι υπηρεσίες υποβάθρου είναι περιορισμένες — η σάρωση PendingIntent παρακάμπτει αυτόν τον περιορισμό μέσω BroadcastReceiver, τον οποίο το σύστημα μπορεί να εκκινήσει κατά τη λήψη συμβάντος BLE. Σε Android 10+ (API 29) η σάρωση BLE υποβάθρου περιορίζεται περαιτέρω από πολιτικές εξοικονόμησης ενέργειας κατασκευαστών (Xiaomi, Huawei, Samsung μπλοκάρουν λειτουργίες BLE υποβάθρου). Για κρίσιμα σενάρια BLE, απαιτείται ειδοποίηση με foreground service.

kotlin
// Σάρωση BLE υποβάθρου μέσω PendingIntent
class BackgroundBLEScanner(private val context: Context) {

    private val scanner: BluetoothLeScanner? by lazy {
        val adapter = BluetoothAdapter.getDefaultAdapter()
        adapter?.bluetoothLeScanner
    }

    fun startBackgroundScan() {
        // Δημιουργία PendingIntent για BroadcastReceiver
        val intent = Intent(context, BLEBroadcastReceiver::class.java)
        val pendingIntent = PendingIntent.getBroadcast(
            context,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        // Ρυθμίσεις σάρωσης υποβάθρου
        val settings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
            .setMatchMode(ScanSettings.MATCH_MODE_STICKY)
            .build()

        // Έναρξη σάρωσης υποβάθρου
        scanner?.startScan(
            null,  // φίλτρα
            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) {
        // Λήψη αποτελεσμάτων σάρωσης
        val results = BluetoothLeScanner.getPendingIntentScanResults(intent)
        results?.let { scanResults ->
            for (result in scanResults) {
                // Αποστολή ειδοποίησης στον χρήστη
                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 ξεκινά σάρωση BLE υποβάθρου μέσω PendingIntent. Το startBackgroundScan δημιουργεί ένα PendingIntent που κατά την ανίχνευση συσκευής BLE στέλνει Broadcast στο BLEBroadcastReceiver. Το BroadcastReceiver εξάγει ScanResult μέσω getPendingIntentScanResults() και μπορεί να εμφανίσει ειδοποίηση ή να στείλει δεδομένα στον διακομιστή. Αυτή η προσέγγιση λειτουργεί ακόμη και αν η εφαρμογή τερματίστηκε από το σύστημα — το Android εκκινεί το BroadcastReceiver κατά τη λήψη broadcast.

Παράδειγμα σαρωτή BLE σε Kotlin με BluetoothLeScanner

Πλήρες παράδειγμα σαρωτή BLE σε Kotlin, που χρησιμοποιεί BluetoothLeScanner με ScanSettings, ScanFilter και ScanCallback για εύρεση συσκευών Heart Rate Monitor. Ο σαρωτής εμφανίζει λίστα ευρεθέντων συσκευών με RSSI και UUID υπηρεσιών, με δυνατότητα σύνδεσης μέσω BluetoothGatt.

kotlin
// Πλήρης σαρωτής BLE με coroutines σε Kotlin
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
        if (adapter?.isEnabled != true) {
            close(IllegalStateException("Bluetooth disabled"))
            return@callbackFlow
        }

        // Διαμόρφωση σάρωσης
        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"))
            }
        }

        // Έναρξη σάρωσης
        scanner?.startScan(filters, settings, callback)

        // Αυτόματη διακοπή μετά από διάρκεια
        delay(duration)
        scanner?.stopScan(callback)
        close()
    }.flowOn(Dispatchers.IO)

    fun stop() {
        scanner?.stopScan(object : ScanCallback() {})
    }
}

class BLEException(message: String) : Exception(message)

Η κλάση DeviceScanner χρησιμοποιεί Kotlin Flow (callbackFlow) για αντιδραστική σάρωση BLE. Η σάρωση ξεκινά με ρυθμίσεις LOW_LATENCY και φίλτρο βάσει UUID Heart Rate Service. Τα αποτελέσματα εκπέμπονται μέσω onScanResult στο Flow. Αυτόματη διακοπή μετά από καθορισμένη διάρκεια (10 δευτερόλεπτα προεπιλογή). Το FlowOn(Dispatchers.IO) μεταφέρει λειτουργίες BLE σε νήμα υποβάθρου. Αυτή η προσέγγιση επιτρέπει τη χρήση σάρωσης BLE σε αρχιτεκτονική MVVM μέσω viewModelScope.launch και collect.

Συχνές Ερωτήσεις

Τι είναι το BluetoothLeScanner στο Android;

BluetoothLeScanner — κλάση Android (API 21+) για σάρωση BLE. Λαμβάνεται μέσω BluetoothAdapter.getBluetoothLeScanner(). Υποστηρίζει τρεις λειτουργίες σάρωσης (LOW_POWER, BALANCED, LOW_LATENCY), φιλτράρισμα βάσει UUID, ονόματος και MAC διεύθυνσης, αποτελέσματα παρτίδας και PendingIntent για σάρωση υποβάθρου. Αντικαθιστά την παρωχημένη μέθοδο BluetoothAdapter.startLeScan().

Ποια είναι η διαφορά μεταξύ LOW_POWER και LOW_LATENCY;

SCAN_MODE_LOW_POWER — λειτουργία υποβάθρου με καθυστέρηση ανίχνευσης 5–10 δευτερόλεπτα, ελάχιστη κατανάλωση ενέργειας. SCAN_MODE_LOW_LATENCY — ενεργή λειτουργία με καθυστέρηση περίπου 100 ms, μέγιστη κατανάλωση ενέργειας. SCAN_MODE_BALANCED — συμβιβασμός (~2 δευτερόλεπτα καθυστέρηση). Για σάρωση UI χρησιμοποιήστε LOW_LATENCY, για παρακολούθηση υποβάθρου — LOW_POWER με PendingIntent.

Γιατί το BluetoothLeScanner δεν βρίσκει συσκευές;

Αιτίες: Bluetooth απενεργοποιημένο (ελέγξτε adapter.isEnabled), δεν έχουν χορηγηθεί δικαιώματα (BLUETOOTH_SCAN σε API 31+, ACCESS_FINE_LOCATION σε API 23–30), scanner = null (προσαρμογέας μη διαθέσιμος), συσκευή εκτός εμβέλειας ή χρήση λανθασμένου φίλτρου. Ελέγξτε επίσης το onScanFailed — ο κωδικός σφάλματος θα υποδείξει την αιτία: SCAN_FAILED_ALREADY_STARTED (1) ή SCAN_FAILED_APPLICATION_REGISTRATION_FAILED (2).

Πώς να σαρώσετε BLE στο παρασκήνιο στο Android;

Χρησιμοποιήστε την έκδοση PendingIntent του startScan() — μεταδώστε PendingIntent αντί για ScanCallback. Κατά την ανίχνευση συσκευής BLE, το Android στέλνει Broadcast στο BroadcastReceiver, το οποίο μπορεί να εκκινηθεί από το σύστημα ακόμη και αν η εφαρμογή είναι στο παρασκήνιο. Για Android 8+, προσθέστε το BroadcastReceiver στο manifest. Σε Android 10+, λάβετε υπόψη τους περιορισμούς εξοικονόμησης ενέργειας κατασκευαστών.

Πόσες συσκευές BLE μπορούν να ανιχνευθούν σε μία σάρωση;

Το BluetoothLeScanner δεν έχει όριο στον αριθμό ανιχνεύσιμων συσκευών — ο περιορισμός εξαρτάται από τον κορεσμό BLE του περιβάλλοντος. Σε ένα γραφείο μπορεί να υπάρχουν 20–50 ενεργές συσκευές BLE, σε ένα εμπορικό κέντρο εκατοντάδες. Για φιλτράρισμα, χρησιμοποιήστε ScanFilter (βάσει UUID, ονόματος). Χωρίς φιλτράρισμα, επεξεργαστείτε τα αποτελέσματα ασύγχρονα — το onScanResult μπορεί να κληθεί δεκάδες φορές ανά δευτερόλεπτο.

Περίληψη

  • BluetoothLeScanner — σύγχρονη κλάση Android (API 21+) για σάρωση BLE με ρυθμίσεις και φιλτράρισμα
  • ScanSettings — τρεις λειτουργίες: LOW_POWER (υπόβαθρο), BALANCED (ισορροπημένη), LOW_LATENCY (ενεργή)
  • ScanFilter — φιλτράρισμα βάσει UUID υπηρεσίας, ονόματος συσκευής, MAC διεύθυνσης, δεδομένων κατασκευαστή με AND λογική
  • ScanCallback — onScanResult (μεμονωμένα), onBatchScanResults (παρτίδας), onScanFailed (κωδικοί σφάλματος)
  • PendingIntent — σάρωση υποβάθρου μέσω BroadcastReceiver, λειτουργεί με τερματισμένη εφαρμογή
  • ScanRecord — διαφημιστικά δεδομένα BLE: serviceData, manufacturerSpecificData, advertiseFlags, TX power level
  • Kotlin Flow — το callbackFlow επιτρέπει τη χρήση BluetoothLeScanner σε αντιδραστική αρχιτεκτονική με αυτόματη διακοπή

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης