0

I am using the Google play services for android for requesting periodically the location of a user.

I created a background service that requests the location of a user and saves the location in the device internal storage.

So far everything works.

Now, I want to implement a back-off mechanism : the more stationary a user is, the less frequent the location updates should be ( and vice versa ).

PROBLEM : When I call the method requestLocationUpdates(GoogleApiClient client, LocationRequest request, LocationListener listener, Looper looper) a location request is made instantly ( without taking into account the new value of the interval in the LocationRequest object.

Source code :

/*
 * Copyright (C) 2017 app
 */

package com.app.android.location.service;

import android.Manifest;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.app.android.location.data.LocationEvent;
import com.app.android.location.provider.LocationGson;
import com.app.android.location.provider.LocationHistoryProvider;
import com.app.android.location.utils.LocationUtils;
import com.app.android.share.utils.Logger;


public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

    public static final String TAG = "LocationService";

    private GoogleApiClient mGoogleApiClient;

    private Looper mServiceLooper;
    private ServiceHandler mServiceHandler;

    private LocationHistoryProvider mLocationHistoryProvider;
    private LocationRequest mLocationRequest = LocationRequest.create().setInterval(LocationUtils.DEFAULT_FASTEST_INTERVAL_MILLIS).setMaxWaitTime(LocationUtils.DEFAULT_FASTEST_INTERVAL_MILLIS).setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);


    // Handler that receives messages from the thread
    private final class ServiceHandler extends Handler implements LocationListener {
        ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            buildAndConnectGoogleApiClient();
        }

        @Override
        public void onLocationChanged(final Location location) {

            final LocationEvent newestLocationEvent = new LocationEvent.Builder().setLocation(location).build();

            Logger.warn(TAG, "onLocationChanged() called with: location = [" + newestLocationEvent + "]");
            Logger.warn(TAG, "currentInterval " + LocationUtils.getCurrentFastestInterval());

            LocationUtils.getLatestLocation(mLocationHistoryProvider, new LocationUtils.LocationEventFound() {
                @Override
                public void onLocationEventFound(LocationEvent latestLocationEvent) {
                    if (latestLocationEvent != null && LocationUtils.isLocationUnchanged(latestLocationEvent, newestLocationEvent)) {
                        Logger.debug(TAG, "latestLocationEvent  Found");

                        // compare the newest location to the last location that we saved
                        updateLocationRequests(true);


                    } else {
                        Logger.debug(TAG, "latestLocationEvent Not Found");
                        // if we cannot find a previous item saved, then just save this new item
                        mLocationHistoryProvider.addLocation(newestLocationEvent);
                    }


                }
            });
        }
    }

    @Override
    public void onCreate() {
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread thread = new HandlerThread("ServiceStartArguments",
                Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        // Get the HandlerThread's Looper and use it for our Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);

        mLocationHistoryProvider = LocationGson.getInstance();
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        destroyAndDisconnectGoogleApiClient();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        mServiceHandler.sendMessage(msg);
        return START_STICKY;
    }

    private void destroyAndDisconnectGoogleApiClient() {
        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, mServiceHandler);
            mGoogleApiClient.disconnect();
        }
    }


    protected synchronized void buildAndConnectGoogleApiClient() {
        if (mGoogleApiClient == null) {
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        }

        if (!mGoogleApiClient.isConnected()) {
            mGoogleApiClient.connect();
        }

    }

    private void updateLocationRequests(boolean backoff) {

        if (backoff) {
            long intervalMillis = Math.min(mLocationRequest.getInterval() * 2, LocationUtils.SLOWEST_INTERVAL_MILLIS);
            Logger.warn(TAG, "backoff() called with: fastestIntervalMillis = [" + intervalMillis + "]");
            mLocationRequest.setInterval(intervalMillis);
        }


        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mServiceHandler, mServiceLooper);
        } else {
            Logger.debug(TAG, "permissions for location not granted");
        }

    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        Logger.warn(TAG, "onConnected() called with: bundle = [" + bundle + "]");
        updateLocationRequests(false);
    }

    @Override
    public void onConnectionSuspended(int i) {
        Logger.warn(TAG, "onConnectionSuspended() called with: i = [" + i + "]");
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult result) {
        Logger.warn(TAG, "onConnectionFailed() called with: result = [" + result + "]");
    }

}
Garytech
  • 328
  • 3
  • 6
  • 1
    That's how it's supposed to work. If you don't want the first location update right away (or as soon as available, in practice) then don't request new updates right away. – Markus Kauppinen Mar 27 '17 at 11:07
  • Thanks @MarkusKauppinen, I am thinking of using an alarmmanager and request for the latestknownlocation every X minutes. in that case I have a better control over when I request for a location update. – Garytech Mar 27 '17 at 12:57
  • Just remember that the "last known location" won't be updated unless some application on the device requests location updates. It's of course enough if some other applications do it (you'll get the result then anyway), but you have no control over them. – Markus Kauppinen Mar 27 '17 at 13:35

0 Answers0