Android AltBeacon Background Service Ranging
Asked Answered
T

2

7

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);
    }
}
Tudor answered 28/10, 2014 at 13:24 Comment(0)
G
6

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

Gillespie answered 28/10, 2014 at 14:14 Comment(12)
Hi davidgyoung, I want to scan beacons in background service even though app ui is not visible. Could you please guide me and share code snippet to achieve that. thanks in advanceMirabelle
See example here: altbeacon.github.io/android-beacon-library/samples.html under Starting an App in the Background. You do not need your own service as the library provides one. Just implement callbacks in a custom Application class.Gillespie
Thank you so much.. I tried that but scanning will get stop as soon as if you come out from your application. eg. if you come to home screen. I want to achieve continuous scanning. please help me.Mirabelle
No, scanning will continue at a reduced rate in the background. (Every 5 min by default.) Check out the reference app, which is a working application that does this based on the example above. github.com/AltBeacon/android-beacon-library-reference See here, too altbeacon.github.io/android-beacon-library/battery_manager.htmlGillespie
@Gillespie How to reduce the scanning rate..like you mentioned 5 min by default in earlier comment..So how to reduce it to 1 minDunleavy
beaconManager.setBackgroundBetweenScanPeriod(60000l) tells the library to pause fir 60 seconds between each background scan instead of 5 minutes.Gillespie
@Gillespie I added beaconManager.setBackgroundBetweenScanPeriod(60000l) in my BeaconReference Application class. But I'm not able to achieve it. Question this is the link to my question. Please do share your feedbackDunleavy
I can also confirm that set BackgroundBetweenScanPeriod in the Application Class also doesnt seem to do anything running code in a Nexus 5 with android 6.0Pacifa
You also need to call beaconManager.updateScanPeriods(); to confirm your changes to the scanning service if you make the changes after it has already started. If you make the changes immediately after first getting a reference to the BeaconManager at app start up, you don't need to do this.Gillespie
hey @davidgyoung, I'm implementing Altbeacon in a Titanium app using this module github.com/dwk5123/android-altbeacon-module. It works quite good, but I can't override the Titanium-generated Application class to implement the "BootstrapNotifier" interface. Is there another way to got the "didDetermineStateForRegion" event when the app is killed (swiped off from task manager)? Thank you so much for your work on SO, seen you active here in so many Altbeacon questions =)Kleptomania
This is probably worthy of its own question.Gillespie
Hi @Gillespie I posted my own question: #38856095 Can you assist me on that? Thanks =)Kleptomania
A
1

You should extend the Applicaton class and start region background monitoring like it is explained here (see "Starting an App in the Background").

In order to do the ranging, in the same class implement RangeNotifier:

public class AndroidApp extends Application implements BootstrapNotifier, RangeNotifier {...

Start the ranging in didEnterRegion:

@Override
public void didEnterRegion(Region region) {
    try {
        mBeaconManager.startRangingBeaconsInRegion(region);
    }
    catch (RemoteException e) {
        if (BuildConfig.DEBUG) Log.d(Const.TAG, "Can't start ranging");
    }
}
  1. Implement didRangeBeaconsInRegion method:

    @Override
    public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
        if (beacons.size() > 0) {
            Beacon firstBeacon = beacons.iterator().next();
            if (BuildConfig.DEBUG) Log.d(Const.TAG, "Beacon ranged: UUID: "
                    + firstBeacon.getId1().toString() + " Major: "
                    + firstBeacon.getId2().toString() + " Minor: "
                    + firstBeacon.getId3().toString());
    
            // Do something with the result
    
            // Stop ranging
            try {
                mBeaconManager.stopRangingBeaconsInRegion(region);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
    
Assumpsit answered 17/12, 2015 at 6:3 Comment(3)
Can you please share a sample class of aplication with RangeNotifier?Clipped
why do you need to stop ranging beacons?Desideratum
@DiegoRivera, you need to stop ranging beacons in order to save battery life.Assumpsit

© 2022 - 2024 — McMap. All rights reserved.