Android onLocationChanged not obeying update minimum duration and update older than previous one

203 Views Asked by At

I am experiencing some strange behaviour from monitoring the times of updated locations from the GPS LocationListener.

Sometimes (though intermittently) I will see the updated location have a timestamp a fraction of a second off (despite the minimum update being set to 5000milliseconds in requestUpdates) or sometimes even earlier than the previous location?

Example of both seen as fraction of a second before previous location (logcat output, edited for clarity):

GpsTracker_v4: onLocationChanged: Warning: location update is older than previous location
D/GpsTracker_v4: 
    previous location:
        provider: network
        time:20/02/22 06:07:17.468
    ---
    new location:
        provider: network
        time: 20/02/22 06:07:17.467

I have logged enough by now to (fairly) confidently claim this is not due to a gps/network provider conflict, or due to it only being milliseconds apart (example of longer durations up to seconds long included at bottom of post)

My entire tracker class (minus the dozens of debugging logs):

public class GpsTracker_v4 {
    /*--------------------------------------
        CONSTANTS
    --------------------------------------*/
    private static final String TAG = GpsTracker_v4.class.getSimpleName();

    // location updates interval - 5sec
    private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 5000;


    /*--------------------------------------
        MEMBER VARIABLES
    --------------------------------------*/
    //---VARIABLES---
    Context context;
    LocationManager locationManager;
    LocationListener locationListener;

    Location previousLocation = null;


    /*--------------------------------------
        CONSTRUCTOR
    --------------------------------------*/
    //-create manager and initialise location listener
    public GpsTracker_v4(Context c) {
        this.context = c;

        locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

        locationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {

                if (location != null) {
                    if (previousLocation != null) {

                        //check location update is more recent than previous
                        if (checkUpdateTime(location)) {
                            Log.d(TAG, "onLocationChanged: new location more recent");
                        } else {
                            Log.w(TAG, "onLocationChanged: Warning: location update is " +
                                    "older than previous location");
                        }

                        Log.d(TAG, "previous location:\n" +
                                "provider: " + previousLocation.getProvider() + "\n" +
                                "time:" + getDateString(previousLocation.getTime()) + "\n" +
                                "---\n" +
                                "new location:\n" +
                                "provider: " + location.getProvider() + "\n" +
                                "time: " + getDateString(location.getTime()));

                        //check location is more accurate than previous location
                        if (checkUpdateAccuracy(location)) {
                            Log.d(TAG, "onLocationChanged: location update is more accurate");
                        } else {
                            Log.w(TAG, "onLocationChanged: Warning: location update is " +
                                    "less accurate or equal to previous");
                        }

                        Log.d(TAG, "Prev location:\n" +
                                "provider: " + previousLocation.getProvider() + "\n" +
                                "accuracy: " + previousLocation.getAccuracy() + "\n" +
                                "---\n" +
                                "New location:\n" +
                                "provider: " + location.getProvider() + "\n" +
                                "accuracy: " + location.getAccuracy());
                    } else {
                        //no previous location yet
                        Log.d(TAG, "onLocationChanged: previousLocation is null: " +
                                "using first location update:");
                    }

                    //replace previous location with updated
                    previousLocation = location;
                    //debugging attempt: remove all content of location 
                    location = null;
                } else {
                    //null location passed
                    Log.e(TAG, "onLocationChanged: Error: passed location is NULL");
                    //todo: handle
                }
            }

            @Override
            public void onStatusChanged(String s, int i, Bundle bundle) {
                //todo:
            }

            @Override
            public void onProviderEnabled(String s) {
                //todo:
            }

            @Override
            public void onProviderDisabled(String s) {
                //todo:
            }
        };

        beginRequestUpdates();
    }


    /*--------------------------------------
        METHODS
    --------------------------------------*/
    //-begin requesting updates from both gps and network providers
    public void beginRequestUpdates() {
        //permission check (IDE insists its done here, not earlier or in another method)
        if (ContextCompat.checkSelfPermission(context,
                Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(
                context, Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            Log.e(TAG, "checkPermission: Error: Permission not been granted.);
            return;
        }

        //begin GPs updates
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
                UPDATE_INTERVAL_IN_MILLISECONDS,
                0,
                locationListener,
                Looper.getMainLooper());
        Log.d(TAG, "beginRequestUpdates: GPS updates requested");

        //begin network updates
        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
                UPDATE_INTERVAL_IN_MILLISECONDS,
                0,
                locationListener,
                Looper.getMainLooper());
        Log.d(TAG, "beginRequestUpdates: Network updates requested");
    }


    /*--------------------------------------
        HELPER METHODS
    --------------------------------------*/
    //-return true if time of location update is more recent than previous update time
    public boolean checkUpdateTime(Location location) {
        return location.getTime() > previousLocation.getTime();
    }


    //-return true if location update is more accurate than previous location
    public boolean checkUpdateAccuracy(Location location) {
        return location.getAccuracy() < previousLocation.getAccuracy();
    }


    //-get user-readable date/time from milli (in year-month-day format)
    private String getDateString(long milliSeconds) {
        String dateFormat = "yy/MM/dd hh:mm:ss.SSS";
        SimpleDateFormat formatter = new SimpleDateFormat(dateFormat);

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(milliSeconds);
        return formatter.format(calendar.getTime());
    }


    public void printLocation(Location location) {
        System.out.println("" +
                "Provider: " + location.getProvider() + "\n" +
                "Latitude: " + location.getLatitude() + "\n" +
                "Longitude: " + location.getLongitude() + "\n" +
                "Accuracy: " + location.getAccuracy() + "\n" +
                "Time: " + getDateString(location.getTime()) + "\n");
    }


    /*--------------------------------------
        MUTATORS
    --------------------------------------*/
    //-set listener from outside class
    public void setListener(LocationListener listener) {
        this.locationListener = listener;
    }
}

What is the reason for this behaviour?

Edit

Here is a better example of new location update being timestamped prior to previous:

updated location:
    Time: 20/02/22 06:44:57.346
previous location:
    Time: 20/02/22 06:45:21.370
0

There are 0 best solutions below