Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make RegionBootstrap easier to use #151

Closed
davidgyoung opened this issue Apr 3, 2015 · 9 comments
Closed

Make RegionBootstrap easier to use #151

davidgyoung opened this issue Apr 3, 2015 · 9 comments

Comments

@davidgyoung
Copy link
Member

In order for RegionBootstrap to work properly, it must be put in the Application class onCreate method, and must be instantiated before any binding operations are done on the BeaconManager. This needs to be simplified so it is harder to misconfigure.

@davidgyoung
Copy link
Member Author

A few ideas for improvements:

@Crashthatch
Copy link

Why MUST the RegionBootstrap be created inside a custom Application class? I'm trying to add background beacon detection into a cordova plugin (which should not require taking control of the Application class). What's the technical reason for not supporting creating RegionBootstraps within a Service?

Details here: petermetz/cordova-plugin-ibeacon#141

@davidgyoung
Copy link
Member Author

Good question, @Crashthatch. The RegionBootstrap must be instantiated when the app is first launched, and a reference to the RegionBootstrap must be held as long as the app remains running so it does not get garbage collected. The custom Application class is a very convenient way to do this, but there are other possibilities.

The AndroidBeaconLibrary registers a BroadcastReceiver to be called at boot time or power connect/disconnect that starts up the scanning service if the app is not running already. When this happens, the Android lifecycle also instantiates the custom Application class and calls onCreate, which makes it a very convenient place to set up the RegionBootstrap.

At the same time the RegionBootstrap gets instantiated, you must also register with it the regions you wish to look for. For this reason, the application programmer needs to be able to execute code that sets up the specific regions at the time the RegionBootstrap is created.

If you want to construct the RegionBootstrap a custom service here are a few tips:

  1. Make sure that service starts up automatically when the app does, at boot time, and stays alive with a reference to the RegionBootstrap for the duration of the app.
  2. When you construct a RegionBootstrap, make some kind of callback to an application programmer method so the programmer can set up the regions to be monitored.

@markvandertol
Copy link
Contributor

It shouldn't be too hard to restructure the code that the Beacon Library is able to run autonomously as its own Service. Then you no longer need a class extending from android.app.Application. The service can then keep itself alive by system Intents, scheduled Intents and Intents created in Activitys. We have a service designed this way and have no problem keeping it alive.

Instead of the developer being responsible for pushing the configuration such as for what kind of region to monitor, the service has to detect that itself. This could be done by making done by making the service abstract or detecting this information using reflection. I think that the public interface also will become simpler, since users of the library no longer have to bind with services or have to change state in static fields/singletons themselves as for example is the case for BeaconParsers in the BeaconManager. It will then be easier to hide such complexities inside the library, while exposing a much simpler interface.

I could work on a prototype if you're interested somewhere in the coming week.

@davidgyoung
Copy link
Member Author

Understand that this library already does run autonomously as a service. That's what the BeaconService class does, and it doesn't require a custom Application class to be defined to do so. It gets started by the StartupBroadastReceiver class. This broadcast receiver gets called when the phone boots, which is when scanning starts (if any beacon consumer is defined at that time.)

The purpose of the RegionBootstrap class is to provide a hook for the application programmer to specify which beacons the application should be looking for, and which classes should receive a notification when this happens. The key to any new API is defining a replacement hook for specifying these regions when the phone boots and the app starts up in the background to look for beacons.

In terms of simplifying this and making it easier to use, I'm open to suggestions for a design. I'd like any new API design to meet the following goals:

  • Work alongside the existing RegionBootstrap for backward compatibility (which may be deprecated until the 3.0 release)
  • Not make breaking changes with other existing APIs.
  • Be more analogous to the way background beacon detections work on iOS (or at least not less so), so folks doing cross-platform development will work with similar APIs.
  • Be easier for folks to understand and set up with new beacon applications.
  • Not require the application using the library to define a custom application class or any new services.

One thought I had is to store any defined BeaconRegions in shared preferences (or other non-volatile storage) and simply restart monitoring and ranging for those BeaconRegions when the app restarts. But the library would still need a reference of what class to call for callbacks when beacons are discovered. On iOS, this is handled by the AppDelegate (the rough equivalent of which is the much less used custom Application class on Android.) Perhaps it would be possible to specify to the library the class that receives these callbacks, store the classname in nonvolatile storage, then use reflection to instantiate it when the app starts back up. The disadvantage of this design is that the callback class could no longer be a simple interface. It would have to have an empty constructor (or one with pre-defined signature) so it could be instantiated by the library.

@Crashthatch
Copy link

I like that design- takes a lot of the maintenance out of the user's hands and puts it into the Altbeacon library for the 90% use-case where the user just wants to resume monitoring for beacons on boot / after app is closed. I don't see using an abstract class instead of an interface as a huge problem. Only possible situation might be where someone would need their notifier to extend another base-class (and so be unable to also inherit from our abstract BootstrapNotifier class)? Do you think this is a common occurrence? Are there any other classes in the altbeacon library that a notifier might want to extend for other functionality (I'm not that familiar with the rest of the library)?

I think we'd also need to persist some of the BeaconManager settings to the nonvolatile storage too (like the beaconParsers and fore/background time settings too) so that we can restore the same search parameters after reboot.

Possibly could use .scanOnBoot / .stopScanningOnBoot methods on the BeaconRegion to add/remove it from the shared preferences, enabling / disabling search for that region on boot. This is backwards compatible with RegionBootstrap as regions would still be created as non-resuming-on-boot until .scanOnBoot was called (and RegionBootstrap's constructor could just wrap this call). Maybe it even makes sense to put these into MonitorNotifier instead and remove BootstrapNotifier all togther?

I haven't done a huge amount of development with beacons on iOS, but it was my impression that iOS just starts monitoring in the background automatically with no further action from the user, and makes the same callbacks as it does when the app is running.

@davidgyoung
Copy link
Member Author

Yes, I agree that the other settings you mention would also have to be saved to non-volatile storage.

Here's the thing about how iOS does it:

Every application in iOS has an AppDelegate with a didFinishLaunchingWithOptions method (the rough equivalent of a custom Android Application class' onCreate method.) The way it works on iOS, is that the app gets launched by the OS on beacon detection, and if the AppDelegate defines a didEnterRegion callback, it gets called as well.

We could do the same on Android with a custom Application class, but I understand that this would not work well for Cordova, so we need to have a second option. What is considered the best practice on Cordova for initialization events that happen before any UI is displayed?

@danielesegato
Copy link

Hi,

I'm completely against an abstract Application class. This also assume I know the region to scan / beacon layout at compile time.

I wish the boot receiver was something I had to register in the manifest, so that I could implement my own version of it with my initialization and detaching the beacon initialization from the Application class.

I receive the region to parse from a remote service and I sync it locally in a local database. Thus I need to initialize beacon in background and at runtime.

Furthermore: I think the way the library currently work for handling background events is wrong: it should use PendingIntent and call them when the app enter a beacon zone.

Look at the FusedLocation API by google. It has an online api where you register a listener and you had to keep a static reference to that listener all the time (this is meant to be used from an Activity, or UI anyway).

Then there's the background implementation that requires a PendingIntent. After registering a PendingIntent your Service/Activity can be stopped and the intent is sent to the app waking it up.

Also: if you run a service in background and want it to consistently keep running all the time you have to use wake lock permission to avoid the device going to sleep.
This is really bad anyway! Because that's how your device save the most of its battery while you don't use it.
If you do not use wake lock the system can kill your service any time: usually when it has low resources and need to free some. This means your service can go down and not come up again until the user open the app or reboot the device.

So an always running service is a bad thing. You should instead use Intent and Alarms (or new Job Sheduler) to wake your service up and check for beacons.

This means every configuration for the beacon must be stored somewhere independent to app initialization.

@davidgyoung
Copy link
Member Author

closed per #1046

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants