Android AltBeacon Background Service Ranging

mashrur picture mashrur · Oct 28, 2014 · Viewed 8.3k times · Source

I am working on a bluetooth app using the AltBeacon library. It seems that only on instance of the BeaconManager is allowed per application. The problem I am facing is this: I want a continuously running background service that constantly does bluetooth ranging and sends notifications. If I open my app (bring it to foreground) I was the service to pause ranging. The foreground activity will then do ranging and show content on the screen.

The problem is that the beacons manager (from BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);) in the activity and service is the same instance. So when the activity gets closed, beaconManager.unbind(this); gets called and the range notifier in the service no longer fires.

Is it possible to get two separate instances of beacon manager? If not, how can I do ranging in a continuous running service and an activity?

RangingActivity

@Override
protected void onCreate(Bundle savedInstanceState) {
...
regionEstimote = new Region("estimote", null, null, null);
beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
beaconManager.bind(this);
}
@Override
protected void onDestroy() {
    super.onDestroy();
    notificationManager.cancel(NOTIFICATION_ID);
    //beaconManager.unbind(this);
}
@Override
public void onBeaconServiceConnect() {
    beaconManager.setRangeNotifier(new RangeNotifier() {
        ....
    });
    try {
        beaconManager.startRangingBeaconsInRegion(regionEstimote);
    } catch (RemoteException e) {
        Log.e(TAG, "RangingActivity", e);
    }
}

BeaconService.java

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if(beaconHistory == null)
        beaconHistory = new HashMap<Integer, Date>();

    mBeaconManager = BeaconManager.getInstanceForApplication(this);
    mBeaconManager.getBeaconParsers().add(new BeaconParser().
            setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));

    return START_STICKY;
}

@Override
public void onDestroy() {
    super.onDestroy();
    beaconHistory = null;
    mBeaconManager.unbind(this);
}
@Override
public void onBeaconServiceConnect() {
    mBeaconManager.setRangeNotifier(new RangeNotifier() {
        @Override
        public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
            if(ActivityBase.isActivityVisible()) {   //don't do ranging logic if any activity from my app is in the foreground
                return;
            }
            ...
        }
    });

    try {
        mBeaconManager.startRangingBeaconsInRegion(regionMint);
    } catch (RemoteException e) {
        Log.e(TAG, "BeaconService", e);
    }
}

Answer

davidgyoung picture davidgyoung · Oct 28, 2014

This is a case where a custom android.app.Application class is very useful. The BeaconManager is a singleton so only one is allowed to exist at the same time. Similarly, the Application class has a single instance per active Android application. If you want to do beacon detection in an Activity and a Service simultaneously, use the centralized Application class to do the binding to BeaconManager and then forward the callbacks to both your Activity and your Service.

You can see an example of binding to the BeaconManager in an Application class and then passing callbacks to an Activity in the reference application here: https://github.com/AltBeacon/android-beacon-library-reference/blob/master/app/src/main/java/org/altbeacon/beaconreference/BeaconReferenceApplication.java