Search code examples
androidibeaconibeacon-androidandroid-ibeacon

AltBeacon : onBeaconServiceConnect not called when BeaconConsumer Binded From DidEnterRegion BootstrapNotifier callback


I'm writing an Android App custom Class that utilises AltBeacon's Android-Beacon-Library.

Instead of posting the class, I am reproducing the problem using the reference implementation samples provided with the library.

If I activate both monitoring (by implementing a RegionBootstrap) and ranging beaconManager.bind(new BeaconConsumer()...) directly in onCreate(), everything works perfectly.

I would like however to start ranging only when a Beacon enters region, so in didEnterRegion(Region arg0) callback of the RegionBootstrap, but when I do so, onBeaconServiceConnect() is never called. I believe doing it this way would be more efficient in terms of battery consumption.

I have tried force executing on the main thread as well as using delayed timers but it didn't work.

Anyone could get this to work ? Any help appreciated!

public class BeaconReferenceApplication extends Application implements BootstrapNotifier {

    private static final String TAG = "AndroidProximityReferenceApplication";
    private RegionBootstrap regionBootstrap;
    private BackgroundPowerSaver backgroundPowerSaver;
    BeaconManager beaconManager;
    Context context = this;

    public void onCreate() {
        super.onCreate();
        beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
        // Setup beacon layout - should be done once only
        List<BeaconParser> bp = beaconManager.getBeaconParsers();
        bp.add(new BeaconParser().setBeaconLayout("m:0-3=4c000215,i:4-19,i:20-21,i:22-23,p:24-24"));

        Region region = new Region("backgroundRegion", null, null, null);
        regionBootstrap = new RegionBootstrap(this, region);
        backgroundPowerSaver = new BackgroundPowerSaver(this);

        // THIS WORKS
        bindBeaconConsumer();
    }

    public void bindBeaconConsumer() {
        beaconManager.bind(new BeaconConsumer() {
            @Override
            public void onBeaconServiceConnect() {
                // ISSUE : Does not pass here when bindBeaconConsumer() is called from didEnterRegion

                // Set Range Notifier and start ranging
            }

            @Override
            public Context getApplicationContext() {
                return context;
            }

            @Override
            public void unbindService(ServiceConnection serviceConnection) {
                context.unbindService(serviceConnection);
            }

            @Override
            public boolean bindService(Intent intent, ServiceConnection serviceConnection, int i) {
                return context.bindService(intent, serviceConnection, i);
            }
        });
    }

    @Override
    public void didEnterRegion(Region arg0) {
        Log.d(TAG, "did enter region.");

        // THIS DOESN'T WORK : onBeaconServiceConnect() never called
        // bindBeaconConsumer();
    }

    @Override
    public void didExitRegion(Region region) {
    }

    @Override
    public void didDetermineStateForRegion(int state, Region region) {
    }
}

Solution

  • The reason calling bindBeaconConsumer() doesn't work in the monitoring callback is because by the time it executes, the class is already bound to the service. If you look at LogCat, you will see a warning message about this. Since it is already bound, the code does not get a callback to onBeaconServiceConnect().

    Why is it already bound? Because of the line below:

    regionBootstrap = new RegionBootstrap(this, region);
    

    The RegionBootsrap class does a bind operation behind the scenes, which executes in a second or so.

    The easiest way to fix this is to remove the call to bindBeaconConsumer() in the didEnterRegion method and instead just call beaconManager.startRangingBeaconsInRegion(...). You don't need to bind again because by that point, the service is already bound.