For my specific use case, I need to fetch the device's real-time location updates and pass them to a remote server at 15 15-second frequency(or less than 15 seconds). To achieve this, I wrote a foreground service that initializes a FusedLocationProviderClient with interval and fastestIntetval as 15s. I am using an external HandlerThread's looper to invoke requestLocationUpdates.

When the app is in the foreground, location updates are received in onLocationResult callback of LocationCallback and also if I invoke getLastLocation() then it returns the same location. But whenever the app is minimized (service still running, the foreground notification is there), onLocationResult does not gets triggered and invoking getLastLocation() returns null.

Is this expected behavior from Fused? If not, how can I fix this issue. I am sharing the codes bellow:

    private val mLocationRequest =
        LocationRequest().apply {
            interval = updateFrequency
            fastestInterval = updateFrequency
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }
    private val mFusedLocationClient: FusedLocationProviderClient by lazy {
        LocationServices.getFusedLocationProviderClient(
            this
        )
    }
    private val mLocationCallback: LocationCallback by lazy {
        object : LocationCallback() {
            override fun onLocationResult(p0: LocationResult?) {
                super.onLocationResult(p0)
                p0?.lastLocation?.let {
                    if (System.currentTimeMillis() - (Constants.lastLocationFetchTimestamp
                            ?: 0) > minTimeBlockThreshold && Constants.currentLocation != null
                    ) {
                        
                    }
                }
            }
        }
    }
    private lateinit var mServiceHandler: Handler

    override fun onCreate() {
        super.onCreate()
        getLastLocation()
        val handlerThread = HandlerThread(LocationUpdatesService::class.java.simpleName)
        handlerThread.start()
        mServiceHandler = Handler(handlerThread.looper)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)
        return START_NOT_STICKY
    }

    override fun onBind(p0: Intent): IBinder {
        super.onBind(p0)
        stopForeground(true)
        return mBinder
    }

    override fun onRebind(intent: Intent?) {
        super.onRebind(intent)
        stopForeground(true)
    }

    override fun onUnbind(intent: Intent?): Boolean {
        if (!mChangingConfiguration && preferenceManager.requestingLocationUpdates()) {
            startForeground(
                (applicationContext as ContractorApp).notificationIdForForegroundService,
                NotificationUtils.getForegroundServiceNotification(applicationContext)
            )
        }
        return true
    }

    override fun onDestroy() {
        super.onDestroy()
        mServiceHandler.removeCallbacksAndMessages(null)
    }

    fun requestLocationUpdates() {
        try {
            mFusedLocationClient.removeLocationUpdates(mLocationCallback)
            preferenceManager.setRequestingLocationUpdates(true)
            startService(Intent(applicationContext, LocationUpdatesService::class.java))
            mFusedLocationClient.requestLocationUpdates(
                mLocationRequest,
                mLocationCallback, mServiceHandler.looper
            )
            startRecursiveForceLocationFetchCall()
            Constants.locationUpdatesRequested = true
        } catch (unlikely: SecurityException) {
            Utils.reportNonFatals(unlikely)
            preferenceManager.setRequestingLocationUpdates(false)
        }
    }

    fun removeLocationUpdates() {
        try {
            Constants.locationUpdatesRequested = false
            mFusedLocationClient.removeLocationUpdates(mLocationCallback)
            preferenceManager.setRequestingLocationUpdates(false)
            preferenceManager.remove(Constants.ACTIVE_VEHICLE_TYPE)
            stopRecursiveForceLocationFetchCall()
            stopSelf()
        } catch (unlikely: SecurityException) {
            Utils.reportNonFatals(unlikely)
            preferenceManager.setRequestingLocationUpdates(true)
        }
    }

    fun updateNotificationText() {
        (getSystemService(NOTIFICATION_SERVICE) as NotificationManager).notify(
            (applicationContext as ContractorApp).notificationIdForForegroundService,
            NotificationUtils.getForegroundServiceNotification(applicationContext)
        )
    }

    private fun getLastLocation() {
        try {
            mFusedLocationClient.lastLocation
                ?.addOnCompleteListener { task ->
                    if (task.isSuccessful && task.result != null) {
                        if (task.result != null) {
                            Constants.currentLocation = task.result
                        }
                    }
                }
        } catch (unlikely: SecurityException) {
            Utils.reportNonFatals(unlikely)
        }
    }

    private fun onNewLocation(location: Location) {
        // logic to send location to remote server
    }
    private var forcePing: Job? = null

    private fun startRecursiveForceLocationFetchCall() {
        if (forcePing == null) {
            forcePing = GlobalScope.launch(Dispatchers.IO){
                while (true) {
                    if (ContextCompat.checkSelfPermission(
                            this@LocationUpdatesService,
                            Manifest.permission.ACCESS_FINE_LOCATION
                        ) == PackageManager.PERMISSION_GRANTED
                    ) {
                        mFusedLocationClient.lastLocation.addOnSuccessListener { location ->
                            if (location != null) {
                                // Use the last known location
                                val latitude = location.latitude
                                val longitude = location.longitude
                                onNewLocation(location)
                            } else {
                                Timber.tag("FORCE_PING").d("Last known location is null")
                            }
                        }.addOnFailureListener {
                            Utils.reportNonFatals(it)
                        }.addOnCanceledListener {
                            Timber.tag("FORCE_PING").d("Issue")
                        }
                    } else {
                        // No permission
                    }
                    delay(TimeUnit.SECONDS.toMillis(15))
                }
            }
        } else {
            forcePing?.cancel()
            forcePing = null
            startRecursiveForceLocationFetchCall()
        }
    }

    private fun stopRecursiveForceLocationFetchCall() {
        if (forcePing != null) {
            forcePing?.cancel()
            forcePing = null
        }
    }
0

There are 0 best solutions below