AltBeacon / android-beacon-library

Allows Android apps to interact with BLE beacons
Apache License 2.0
2.84k stars 836 forks source link

Recevied exit from region when app is killed by user #861

Closed Draptol closed 5 years ago

Draptol commented 5 years ago

Hi, I have a problem (or a Bug) with your library. I am preparing a small library in which one module scanns beacons in background and foreground. I found your sample app on Github and I tested your code (application class) and everything is ok. After user goes to the task switcher and swipes an app off the screen, scanning is being continued in background.

This is my Application class:

class MyApplication : Application(), BootstrapNotifier, RangeNotifier {

    private val region = Region("backgroundRegion", null, null, null)
    private lateinit var regionBootstrap: RegionBootstrap
    private lateinit var backgroundPowerSaver: BackgroundPowerSaver
    private lateinit var beaconManager : BeaconManager

    override fun didDetermineStateForRegion(status: Int, region: Region?) {
        when (status) {
            MonitorNotifier.INSIDE -> {
                try {
                    beaconManager.startRangingBeaconsInRegion(region!!)
                } catch (t: Throwable) {
                    Log.e("BeaconService", "Can't start ranging beacons in region", t)
                }
            }
            MonitorNotifier.OUTSIDE -> {
                try {
                    beaconManager.stopRangingBeaconsInRegion(region!!)
                } catch (t: Throwable) {
                    Log.e("BeaconService", "Can't stop ranging beacons in region", t)
                }
            }
            else -> {
                Log.d("BeaconService", "Wrong status value: $status")
            }
        }
    }
    override fun didEnterRegion(region: Region?) {
        Log.d("BeaconService","Enter region: ${region?.toString()}")
    }
    override fun didExitRegion(region: Region?) {
        Log.d("BeaconService","Exit region: ${region?.toString()}")
    }
    override fun didRangeBeaconsInRegion(beacons: MutableCollection<Beacon>?, region: Region?) {
        if (beacons != null && beacons.isNotEmpty()) {
            Log.d("BeaconService","Beacons list size: ${beacons.size}")
        } else {
            Log.d("BeaconService","Beacons list is null or empty")
        }
    }
    override fun onCreate() {
        super.onCreate()
        beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this)
        beaconManager.beaconParsers.addAll(createBeaconParsers())
        beaconManager.backgroundBetweenScanPeriod = 10 * 1000
        beaconManager.foregroundBetweenScanPeriod = 20 * 1000
        regionBootstrap = RegionBootstrap(this, region)
        beaconManager.addRangeNotifier(this)
    }
    private fun createBeaconParsers() = arrayOf(
        BeaconService.EDDYSTONE_URL
    ).map { BeaconParser().setBeaconLayout(it) }.toTypedArray()
}

and I am getting these logs in logcat:

2019-04-18 11:29:53.479 7786-7786/com.xxx.xxx I/BeaconService: beaconService version 2.16.1 is starting up on the main process
2019-04-18 11:29:53.481 7786-7786/com.xxx.xxx I/BeaconService: longScanForcingEnabled to keep scans going on Android N for > 30 minutes
2019-04-18 11:29:53.672 7786-7786/com.xxx.xxx I/BeaconService: starting with intent Intent { cmp=com.xxx.xxx/org.altbeacon.beacon.service.BeaconService }
2019-04-18 11:29:53.672 7786-7786/com.xxx.xxx I/BeaconService: binding
2019-04-18 11:29:53.785 7786-7786/com.xxx.xxx I/BeaconService: start monitoring received
2019-04-18 11:29:53.800 7786-7786/com.xxx.xxx I/BeaconService: start ranging received
2019-04-18 11:29:53.802 7786-7786/com.xxx.xxx I/BeaconService: set scan intervals received
2019-04-18 11:29:54.941 7786-7786/com.xxx.xxx D/BeaconService: Beacons list size: 1
2019-04-18 11:30:04.978 7786-7786/com.xxx.xxx D/BeaconService: Beacons list size: 1
2019-04-18 11:30:08.047 1575-3145/? W/ActivityManager: Scheduling restart of crashed service com.xxx.xxx/org.altbeacon.beacon.service.BeaconService in 1000ms
2019-04-18 11:30:09.094 1575-1725/? I/ActivityManager: Start proc 8315:com.xxx.xxx/u0a299 for service com.xxx.xxx/org.altbeacon.beacon.service.BeaconService
2019-04-18 11:30:09.396 8315-8315/? I/BeaconService: beaconService version 2.16.1 is starting up on the main process
2019-04-18 11:30:09.398 8315-8315/? I/BeaconService: longScanForcingEnabled to keep scans going on Android N for > 30 minutes
2019-04-18 11:30:09.462 8315-8315/? I/BeaconService: starting with null intent
2019-04-18 11:30:09.463 8315-8315/? I/BeaconService: starting with intent Intent { cmp=com.xxx.xxx/org.altbeacon.beacon.service.BeaconService }
2019-04-18 11:30:09.463 8315-8315/? I/BeaconService: binding
2019-04-18 11:30:09.476 8315-8315/? I/BeaconService: start monitoring received
2019-04-18 11:30:09.495 8315-8315/? I/BeaconService: start ranging received
2019-04-18 11:30:09.497 8315-8315/? I/BeaconService: set scan intervals received
2019-04-18 11:30:10.628 8315-8315/com.xxx.xxx D/BeaconService: Beacons list size: 1
2019-04-18 11:30:21.548 8315-8315/com.xxx.xxx D/BeaconService: Beacons list size: 1

But I am creating a library so I can't paste my code inside Application class. Insted of this I need to do something like this MyLibrary.init(this) inside onCreate method in ApplicationClass. So I wrote a BeaconService class with exactly the same code like in application class. I passed to this class application context (I checked that I have inside my BeaconService application context by debugging my code). After that when app isn't killed by swapping off from task switcher evrything works perfect. After killing the app I got exit from region event and I am not able to continue scanning even if the beacon is about 20 cm far from device.

This is my BeaconService class

class BeaconService private constructor(private val appCtx: Context) : BootstrapNotifier, RangeNotifier {

    companion object : SynchronizedLazyBuilder<BeaconService, Context>(::BeaconService) {
        const val EDDYSTONE_URL = "s:0-1=feaa,m:2-2=10,p:3-3:-41,i:4-20v"
    }

    private val region = Region("backgroundRegion", null, null, null)
    private lateinit var regionBootstrap: RegionBootstrap
    private lateinit var backgroundPowerSaver: BackgroundPowerSaver
    private val beaconManager = BeaconManager.getInstanceForApplication(appCtx)

    fun startOrReplace() {
        stop()
        init()
    }

    fun stop() {
        if(beaconManager.isAnyConsumerBound) {
            with(beaconManager) {
                stopRangingBeaconsInRegion(region)
                stopMonitoringBeaconsInRegion(region)
                removeAllMonitorNotifiers()
                removeAllRangeNotifiers()
            }
        } else {
            Timber.d("Beacon manager is not bound to any consumer")
        }
    }

    private fun init() {

        with(beaconManager) {
            beaconParsers.clear()
            beaconParsers.addAll(createBeaconParsers())
            backgroundBetweenScanPeriod = 10 * 1000
            foregroundBetweenScanPeriod = 20 * 1000
            //backgroundScanPeriod = 30 * 1000
            this@BeaconService.regionBootstrap = RegionBootstrap(
                this@BeaconService,
                region
            )
            addRangeNotifier(this@BeaconService)
            applySettings()
        }
       // backgroundPowerSaver = BackgroundPowerSaver(appCtx)
    }

    private fun createBeaconParsers() = arrayOf(
        EDDYSTONE_URL
    ).map { BeaconParser().setBeaconLayout(it) }.toTypedArray()

    // -------------------------------------------
    // BootstrapNotifier / RangeNotifier
    // -------------------------------------------

    override fun getApplicationContext() = appCtx

    override fun didDetermineStateForRegion(status: Int, region: Region?) {
        when (status) {
            INSIDE -> {
                try {
                    beaconManager.startRangingBeaconsInRegion(region!!)
                } catch (t: Throwable) {
                    Timber.e(t, "Can't start ranging beacons in region")
                }
            }
            OUTSIDE -> {
                try {
                    beaconManager.stopRangingBeaconsInRegion(region!!)
                } catch (t: Throwable) {
                    Timber.e(t, "Can't stop ranging beacons in region")
                }
            }
            else -> {
                Timber.d("Wrong status value: $status")
            }
        }
    }

    override fun didEnterRegion(region: Region?) {
        Timber.d("Enter region: ${region?.toString()}")
    }

    override fun didExitRegion(region: Region?) {
        Timber.d("Exit region: ${region?.toString()}")
    }

    override fun didRangeBeaconsInRegion(beacons: MutableCollection<Beacon>?, region: Region?) {
        if (beacons != null && beacons.isNotEmpty()) {
            Timber.d("Beacons list size: ${beacons.size}")
            // TODO
        } else {
            Timber.d("Beacons list is null or empty")
        }
    }

and I am getting these logs in logcat:

019-04-18 11:33:40.106 8731-9001/com.xxx.xxx D/BeaconService: Beacon manager is not bound to any consumer
2019-04-18 11:33:40.260 8731-8731/com.xxx.xxx I/BeaconService: beaconService version 2.16.1 is starting up on the main process
2019-04-18 11:33:40.262 8731-8731/com.xxx.xxx I/BeaconService: longScanForcingEnabled to keep scans going on Android N for > 30 minutes
2019-04-18 11:33:40.484 8731-8731/com.xxx.xxx I/BeaconService: starting with intent Intent { cmp=com.xxx.xxx/org.altbeacon.beacon.service.BeaconService }
2019-04-18 11:33:40.485 8731-8731/com.xxx.xxx I/BeaconService: binding
2019-04-18 11:33:40.641 8731-8731/com.xxx.xxx I/BeaconService: start monitoring received
2019-04-18 11:33:40.660 8731-8731/com.xxx.xxx I/BeaconService: start ranging received
2019-04-18 11:33:40.664 8731-8731/com.xxx.xxx I/BeaconService: set scan intervals received
2019-04-18 11:33:41.788 8731-8731/com.xxx.xxx D/BeaconService: Beacons list size: 1
2019-04-18 11:34:01.545 8731-8731/com.xxx.xxx D/BeaconService: Beacons list size: 1
2019-04-18 11:34:03.866 1575-3146/? W/ActivityManager: Scheduling restart of crashed service com.xxx.xxx/org.altbeacon.beacon.service.BeaconService in 1000ms
2019-04-18 11:34:04.898 1575-1725/? I/ActivityManager: Start proc 9277:com.xxx.xxx/u0a299 for service com.xxx.xxx/org.altbeacon.beacon.service.BeaconService
2019-04-18 11:34:05.177 9277-9277/? I/BeaconService: beaconService version 2.16.1 is starting up on the main process
2019-04-18 11:34:05.179 9277-9277/? I/BeaconService: longScanForcingEnabled to keep scans going on Android N for > 30 minutes
2019-04-18 11:34:05.219 9277-9309/? D/BeaconService: Beacon manager is not bound to any consumer
2019-04-18 11:34:05.263 9277-9277/? I/BeaconService: starting with null intent
2019-04-18 11:34:05.264 9277-9277/? I/BeaconService: starting with intent Intent { cmp=com.xxx.xxx/org.altbeacon.beacon.service.BeaconService }
2019-04-18 11:34:05.264 9277-9277/? I/BeaconService: binding
2019-04-18 11:34:05.277 9277-9277/? I/BeaconService: start monitoring received
2019-04-18 11:34:05.294 9277-9277/? I/BeaconService: start ranging received
2019-04-18 11:34:05.296 9277-9277/? I/BeaconService: set scan intervals received
2019-04-18 11:34:06.439 9277-9277/com.xxx.xxx D/BeaconService: Beacons list is null or empty
2019-04-18 11:34:21.577 9277-9277/com.xxx.xxx D/BeaconService: Exit region: id1: null id2: null id3: null
2019-04-18 11:34:21.577 9277-9277/com.xxx.xxx D/BeaconService: Beacons list is null or empty
2019-04-18 11:34:21.578 9277-9277/com.xxx.xxx I/BeaconService: stop ranging received

You can see in above logs that after killing the app the logs are exactly the same as while using Application class instead of BeaconService class. There is only one little problem that I got event "Exit region: id1: null id2: null id3: null" .

Could you tell me what I am doing wrong or maybe is this a bug ?

Android 7.0 Device: Lg G5

davidgyoung commented 5 years ago

This is very confusing to follow because the library has its own service names BeaconService, which has the same name as your custom service. I cannot tell what lines come from what.

A few points:

  1. The RegionBootstrap class is designed to be used from an Application class. If you cannot use it from there, you should really define your service to be a BeaconConsumer, call beaconManager bind(this) in your service startup, then start monitoring your regions in the onBeaconServuceConnect callback. This is how the library is designed to be used in cases like this. RegionBootstrap us just a helper class for use in Application.

  2. Consider that in Android 8+ you can no longer have long running services unless they are foreground services, so if you ever need your app to run beyond Android 7, this architecture could be a problem.

  3. If you want to press on as-is, you are welcome to do so, but you may run into problems by using RegionBootstrap in a way that is not intended or instructed. I see nothing in the logs that shows RegionBootstrap was constructed after your service restarted. (RegionBootstrap should log debug lines that are for some reason not visible.) You might add your own logging when it is constructed to ensure it is.

  4. This is most certainly an issue with custom app debugging, not a bug in the library (even if there is some kind of underlying bug it is triggered by use of RegionBootstrap in a way the docs say not to use it.) That makes this issue out of scope for this forum. I am happy to help further if you'd like to open a question in StackOverflow.com, but I am closing this issue here to keep this forum dedicated to known library bugs and feature requests.