4

What I want to achieve

I am building an app for a shopping mall, with large amount of beacons installed not quite far away from each other (assume ~20m distance).
When a user walks in this mall, even without opening the app, the app needs to keep scan for beacons. When a beacon is detected, I will then query the server to ask if I need to and what local notification to push to user.

What I planned to do

My original plan was to create a Service, returns START_STICKY in onStartCommand() in order to make sure that the service will restart itself even the app is killed by a task manager.

This Service will keep scanning for beacons.

In fact, this is the existing approach of my colleague. According to this colleague, the service starts almost immediately after the app was killed by a task manager.

The problem

But soon I found that this approach is problematic.
The above approach has now 2 major problems:

My next plan

Although I know that JobScheduler can be used to replace Service, which is also the existing approach of Android Beacon Library, perform a scan every 15-25 minutes absolutely cannot fulfill my requirements, where beacons are very close to each other and therefore beacons need to be scanned frequently.

Therefore I come up with another plan:

  1. Use Android Beacon Library to run background detection
  2. Once the first beacon in filter list has been detected, start a foreground service (which will not be killed even in Android 8) that continuously scan for beacons
  3. When all beacons in the filter has been exited, stop the above foreground service. Android Beacon Library shall resume to its background detection state.

The intentions of this approach are:

  1. Make advantage of Android Beacon Library's background detection which saves battery, according to their documentation
  2. Step away of Android Beacon Library's handling of Android 8 due to its built-in limitation of long scanning interval
  3. Scanning goes on even on Android 8 since I am going to use a foreground service

My major question

By reading documentations, I already know how to scan for beacons in the background.
But how do I make use of Android Beacon Library to scan in a foreground service?
Plus, is there any problem that you can discover in the above approach / Do you have any better suggestions to achieve such requirements?

My other question

In fact according to this post, the background service starts 5 minutes after an app is killed.
But by returning START_STICKY in onStartCommand() of a Service, it is restarted almost immediately.
Then, why will there be a 5 minutes delay, even in pre-Oreo?

Sira Lam
  • 5,179
  • 3
  • 34
  • 68

1 Answers1

3

This approach is sound. The Android Beacon Library 2.15+ natively supports foreground services as a scan mechanism for supporting cases like this on Android 8. See here for more info.

The tricky part is to switch back and forth between using Job Scheduler and a Service to do scanning. I have not tested this, but my suggestion would be to bind manually to the BeaconManager in a custom Application class. Then :

  1. On entering a region, stop monitoring and then unbind the BeaconManager.

  2. Start a custom foreground service

  3. In the foreground service, disable Scan Jobs, then bind to the BeaconManager and start ranging

  4. Once no beacons have been ranged for a time, stop ranging, unbind from the BeaconManager, enable Scan Jobs, bind again and then start monitoring.

  5. Finally, exit the foreground service

On the second question, yes, START_STICKY will very quickly restart a service on most platforms. The library uses a 5 minute timer with the AlarmManager as a backup, which will relaunch the service if a START_STICKY restart fails. Indeed, in typical use, the scanning service restarts much more quickly than five minutes.

davidgyoung
  • 63,876
  • 14
  • 121
  • 204
  • Honestly I was expected you will come to answer, and I am so glad that you did lol. Thank you so much, but allow me to preserve the accept until testing complete! – Sira Lam Mar 05 '18 at 06:09
  • In order to unbind the BeaconManager in step 1, I need to pass in a `BeaconConsumer`. Should my CustomApplication implements both `BootstrapNotifier` and `BeaconConsumer`? If yes, (1) Should I call `beaconManager.startRangingBeaconsInRegion()` inside `onBeaconServiceConnect()`? (2) Should I unbind my `beaconManager` in `BootstrapNotifier`'s `didEnterRegion()`, or `MonitorNotifier`'s `didEnterRegion()`? – Sira Lam Mar 05 '18 at 08:52
  • OK, by looking at the source code of `RegionBootstrap`, I now understand that "stop monitoring and then unbind" is accomplished just by `bootstrap.disable()`. Then, how do I "disable scan jobs" in step 3 and "enable scan jobs" in step 4? It looks like `RegionBootstrap` does not have a `enable()` method; and binding a `BeaconManager` is occurred inside the constructor. Should I simply remove reference to the existing `RegionBootstrap` and construct another new one (And it will "enable scan jobs, bind again and start monitoring?) – Sira Lam Mar 05 '18 at 09:38
  • 1
    Yes, you could create a new RegionBootstrap instance. The lack of an enable() method is simply because it was designed mostly for one time setup use cases. To disable scan jobs, you use `beaconManager.setEnableScheduledScanJobs(false); To enable them, you call with the parameter set to true. By default, 8.0+ will use scan jobs unless you make the configuration change manually. And to be clear, only make this configuration change if you are not currently bound. – davidgyoung Mar 05 '18 at 22:26