You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何借助WorkManager解决AltBeacon应用被杀后扫描中断问题

How to Integrate AltBeacon with WorkManager for Background Scanning

Hey there! Let’s walk through how to get your BeaconWorker playing nicely with the AltBeacon library—especially for those tricky post-app-kill scenarios. I’ve tinkered with similar setups before, so here’s a step-by-step breakdown of what you need to adjust and implement:

1. Initialize AltBeacon Properly in the Worker

Since each Worker run is isolated, you’ll need to initialize BeaconManager every time doWork() executes. Make sure to pass the Worker’s application context and configure settings like scan periods and beacon layouts to match your use case.

2. Implement a Suspended Scan with Result Collection

Your makeScan() function needs to perform a scan and wait for results in a coroutine-friendly way. We’ll use CompletableDeferred to suspend the coroutine until the scan finishes or times out, so we don’t block the Worker thread unnecessarily.

3. Handle AltBeacon Callbacks

You’ll need a RangeNotifier to collect detected beacons and a BeaconConsumer to bind the manager to the Worker’s context. Unlike Activities/Fragments, Workers don’t have a built-in lifecycle, so we’ll bind and unbind manually within the coroutine.

4. Reschedule the Worker Wisely

When rescheduling, pick a reasonable interval—keep Android 8+ background restrictions in mind! Avoid intervals shorter than 15 minutes if you want consistent behavior without triggering system throttling. Also, check if the Worker is still active before rescheduling to avoid duplicate jobs.

Modified BeaconWorker Code

import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import org.altbeacon.beacon.*
import androidx.work.*
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.util.concurrent.TimeUnit

class BeaconWorker(appCtx: Context, workerParameters: WorkerParameters) : CoroutineWorker(appCtx, workerParameters) {
    override val coroutineContext: CoroutineDispatcher = Dispatchers.Default
    // Mutex to prevent concurrent scan runs
    private val scanMutex = Mutex()

    override suspend fun doWork() = supervisorScope {
        // Reschedule first to ensure scanning continuity (if needed)
        if (isActive) {
            enqueNextWork()
        }

        val scanResult = runCatching {
            scanMutex.withLock {
                makeScan()
            }
        }.getOrElse {
            // Handle scan errors (e.g., missing permissions, Bluetooth disabled)
            emptyList()
        }

        // Process your scanned beacons here (e.g., send to server, save to local storage)
        scanResult.forEach { beacon ->
            println("Detected beacon: ID1=${beacon.id1}, ID2=${beacon.id2}, RSSI=${beacon.rssi}")
        }

        Result.success()
    }

    private suspend fun makeScan(): List<Beacon> {
        val deferred = CompletableDeferred<List<Beacon>>()
        val beaconManager = BeaconManager.getInstanceForApplication(applicationContext)

        // Configure your beacon layout(s) — update this to match your beacon type
        beaconManager.beaconParsers.add(BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"))

        // Adjust scan periods to balance power usage and accuracy
        beaconManager.setForegroundScanPeriod(1000L) // Scan for 1 second per cycle
        beaconManager.setForegroundBetweenScanPeriod(0L) // No delay during active scan
        beaconManager.setBackgroundScanPeriod(1000L)
        beaconManager.setBackgroundBetweenScanPeriod(0L)

        val rangeNotifier = RangeNotifier { beacons, _ ->
            // Complete the deferred once we get scan results
            if (!deferred.isCompleted) {
                deferred.complete(beacons)
            }
        }

        val beaconConsumer = object : BeaconConsumer {
            override fun getApplicationContext(): Context = applicationContext
            override fun unbindService(p0: Runnable?) {
                beaconManager.unbindService(this)
            }
            override fun bindService(p0: Intent?, p1: ServiceConnection?, p2: Int): Boolean = false
            override fun onBeaconServiceConnect() {
                try {
                    // Scan all beacons with a wildcard region
                    beaconManager.startRangingBeaconsInRegion(Region("scan-region", null, null, null))
                } catch (e: RemoteException) {
                    if (!deferred.isCompleted) {
                        deferred.completeExceptionally(e)
                    }
                }
            }
        }

        beaconManager.addRangeNotifier(rangeNotifier)
        beaconManager.bind(beaconConsumer)

        // Wait for scan results or timeout after 5 seconds (adjust as needed)
        return withTimeoutOrNull(5000L) {
            deferred.await()
        } ?: emptyList().also {
            // Clean up resources if we timed out
            beaconManager.removeRangeNotifier(rangeNotifier)
            beaconManager.unbind(beaconConsumer)
        }
    }

    private fun enqueNextWork() {
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.NOT_REQUIRED)
            .setRequiresBatteryNotLow(false) // Tweak based on your power needs
            .build()

        val workRequest = OneTimeWorkRequestBuilder<BeaconWorker>()
            .setConstraints(constraints)
            .setInitialDelay(15, TimeUnit.MINUTES) // Comply with Android background limits
            .build()

        WorkManager.getInstance(applicationContext).enqueue(workRequest)
    }
}

Key Notes to Remember

  • Permissions: Ensure your app has the necessary permissions: ACCESS_FINE_LOCATION, BLUETOOTH_SCAN, BLUETOOTH_CONNECT (varies by Android version). For Android 12+, you’ll also need ACCESS_BACKGROUND_LOCATION for reliable background scanning.
  • Battery Optimization: Disable battery optimization for your app if possible—otherwise, Android may pause Worker execution when the device is idle.
  • Scan Tuning: Adjust scan periods to balance battery life and scan frequency. Shorter scans use more power but deliver results faster.
  • Targeted Scanning: If you only care about specific beacons, replace the wildcard region in startRangingBeaconsInRegion with IDs matching your beacons.

内容的提问来源于stack exchange,提问作者Draptol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 06:36:43