-
Notifications
You must be signed in to change notification settings - Fork 838
Description
We are developing an SDK that depends on AltBeacon to detect our trackers (similar to smart tags) nearby. It is expected to run in the background most of the time, so in order to achieve better scan results, I’m testing the intent scan strategy settings:
// By disabling the scan jobs
setEnableScheduledScanJobs(false)
//And enabling intent scan strategy
setIntentScanningStrategyEnabled(true)This works great: since the OS delivers the results, we don’t need to worry about keeping the process running in background to start a scan and listen for it.
However, we are receiving crash reports related to concurrency issues during the initialization of IntentScanStrategyCoordinator. I suspect this happens because we are calling BeaconManager.startRangingBeacons(region) off the main thread. Some sample stack traces:
Exception hl.e0: lateinit property scanState has not been initialized
at org.altbeacon.beacon.service.IntentScanStrategyCoordinator.start (IntentScanStrategyCoordinator.kt:118)
at org.altbeacon.beacon.BeaconManager.bindInternal (BeaconManager.java:457)
at org.altbeacon.beacon.BeaconManager.autoBind (BeaconManager.java:1986)
at org.altbeacon.beacon.BeaconManager.startRangingBeacons (BeaconManager.java:1063)
at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver.startRanging (AltBeaconReceiver.kt)
at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver.subscribe (AltBeaconReceiver.kt)
at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver.access$subscribe (AltBeaconReceiver.kt)
at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver$receive$1.invokeSuspend (AltBeaconReceiver.kt)
at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver$receive$1.invoke (AltBeaconReceiver.kt)
at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver$receive$1.invoke (AltBeaconReceiver.kt)
at kotlinx.coroutines.flow.SafeFlow.collectSafely (Builders.kt:61)
at kotlinx.coroutines.flow.AbstractFlow.collect (Flow.kt:230)
at com.technopartner.technosdk.scan.TrackerScanner$startScan$1.invokeSuspend (TrackerScanner.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:108)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely (CoroutineScheduler.kt:584)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask (CoroutineScheduler.kt:793)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker (CoroutineScheduler.kt:697)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run (CoroutineScheduler.kt:684)
Exception hl.e0: lateinit property scanHelper has not been initialized
at org.altbeacon.beacon.service.IntentScanStrategyCoordinator.processScanResults (IntentScanStrategyCoordinator.kt:176)
at org.altbeacon.beacon.service.IntentScanStrategyCoordinator.performPeriodicProcessing (IntentScanStrategyCoordinator.kt:181)
at org.altbeacon.beacon.service.ScanJob$1.run (ScanJob.java:84)
at java.lang.Thread.run (Thread.java:1012)
Exception java.lang.RuntimeException: Unable to start receiver org.altbeacon.beacon.startup.StartupBroadcastReceiver: java.lang.NullPointerException: Attempt to invoke virtual method 'void ln.g.y()' on a null object reference
at android.app.ActivityThread.handleReceiver (ActivityThread.java:3612)
at android.app.ActivityThread.access$1300 (ActivityThread.java:237)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1796)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loop (Looper.java:214)
at android.app.ActivityThread.main (ActivityThread.java:7050)
at java.lang.reflect.Method.invoke
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:965)
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'void ln.g.y()' on a null object reference
at org.altbeacon.beacon.service.ScanHelper$1.onCycleEnd (ScanHelper.java:313)
at org.altbeacon.beacon.service.IntentScanStrategyCoordinator.processScanResults (IntentScanStrategyCoordinator.kt:176)
at org.altbeacon.beacon.startup.StartupBroadcastReceiver.onReceive (StartupBroadcastReceiver.java:59)
at android.app.ActivityThread.handleReceiver (ActivityThread.java:3603)
Our SDK is initialized in MainApplication.onCreate and starts the BeaconManager on a coroutine context that does not run on the main thread. The concurrency issue seems to occur in IntentScanStrategyCoordinator.ensureInitialized and IntentScanStrategyCoordinator.reinitialize, since they only rely on a boolean flag to verify whether the object is initialized:
fun ensureInitialized() {
if (!initialized) {
initialized = true
scanHelper = ScanHelper(context)
reinitialize()
}
}
fun reinitialize() {
if (!initialized) {
ensureInitialized() // this will call reinitialize
return
}
// code continues...
}If two concurrent threads attempt to initialize the coordinator, one sets the flag, while the other skips the entire initialization block (because the flag is already set).
That said, I have some questions regarding this scenario:
-
Regarding the settings: is it correct to disable scan jobs (
setEnableScheduledScanJobs(false)), or can they remain enabled alongside the intent scan strategy? -
Do you agree with my analysis, or could the issue be happening elsewhere in the code?
-
What would you recommend? Should I migrate the
BeaconManagerinitialization back to the main thread (straightforward and maybe the “right” way to initialize AltBeacon), or should I modify the library so thatIntentScanStrategyCoordinatorinitialization is wrapped in a synchronized block (or some other concurrency protection mechanism)?
Current library version: 2.20.6