Closed devasidmi closed 5 months ago
Two possibilities:
I do understand this is awkward with LiveData observers. I am open to better ideas.
+1 for this, same issue with RangeNotifier also. Currently using a workaround to always discard the first value
I was reading this SO post regarding how other folks have solved the more general problem of ignoring stale info with LiveData. Clearly there are lots of complicating factors that make the right solution dependent on your specific use case of using the data.
I am reluctant to add something to fix one use case that may cause trouble with other use cases we are not thinking about.
A couple of thoughts to summarize the problem:
RegionViewModel.rangedBeacons
not with RegionViewModel.regionState
RegionViewModel.rangedBeacons
returning a stale value comes up when the list of rangedBeacons is not empty -- one of those beacons may have been detected many hours or days ago.If my problem statement above is correct, then it seems the existing API can solve this with a single if statement that checks the age of the beacon detection like this:
val rangingObserver = Observer<Collection<Beacon>> { beacons ->
val rangeAgeMillis = System.currentTimeMillis() - (beacons.firstOrNull()?.lastCycleDetectionTimestamp ?: System.currentTimeMillis())
if (rangeAgeMillis < 10000) {
Log.d(MainActivity.TAG, "Ranged: ${beacons.count()} beacons")
}
else {
Log.d(MainActivity.TAG, "Ignoring stale ranged beacons from $rangeAgeMillis millis ago")
}
}
If zero beacons are detected, then the stale ranging data would still be processed (as no timestamp is known) but it seems like that is a minimal problem, because if you expect to be ranging, a new update should come in shortly. And it is not uncommon when ranging for there to be temporary dropouts in detections where you see no beacons, so most code will handle this situation already.
If this solution is workable, it might be possible to add some helper code to eliminate the boilerplate around the calculation of val rangeAgeMillis = System.currentTimeMillis() - (beacons.firstOrNull()?.lastCycleDetectionTimestamp ?: System.currentTimeMillis())
Many of the "proper" LiveData solutions offered in the referenced SO posts above would require some other if statement in the observer to handle this kind of situation, anyway. So it seems reasonable to use an if statement associated with already existing functionality inside the API.
Thoughts?
Sure that works, thanks for your help.
Here's what I ended up with, still using a RangeNotifier as the beacons collection suffered from the same stale data as LiveData + observer.
For my application I'm running singular, long scans in the foreground, so I've tied the rangeAge to the foregroundScanPeriod.
beaconManager.addRangeNotifier(object : RangeNotifier {
override fun didRangeBeaconsInRegion(beacons: Collection<Beacon>, region: Region) {
val rangeAgeMillis = System.currentTimeMillis() - (beacons.firstOrNull()?.lastCycleDetectionTimestamp ?: System.currentTimeMillis())
if (rangeAgeMillis < beaconManager.foregroundScanPeriod) {
val nearestBeacon = beacons.minByOrNull { it.distance }
processBeacon(nearestBeacon)
return
}
else {
Log.d(TAG, "Ignoring stale ranged beacons from $rangeAgeMillis millis ago")
}
}
})
Glad to hear this worked for you, @brendandodd.
I'm not sure it matters to you, but I suspect any stale data you see with the RangeNotifier has a different cause than what you see with the RegionViewModel.rangedBeacons LiveData model. While LiveData will always hold on to the last known value and will deliver it immediately to newly registered observers, the RangeNotifier won't do that. The stale data you see with RangeNotifier may be caused by your app doing long scans in the foreground, but the UI being blocked from executing when not visible on the screen. This can sometimes cause stale data to be queued for delivery, but not processed by UI elements until the app returns to the foreground.
@devasidmi any thoughts on this solution?
closed due to inactivity
Ranging feature uses LiveData which sends old value on first scan when I'm making new observer, do u have any workarounds? Thanks