2

I've made a running app and for that, I've made a Service class to update user location (removed unimportant code):

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

    private static final String TAG = LocationService.class.getSimpleName();
    protected GoogleApiClient mGoogleApiClient;
    protected LocationRequest mLocationRequest;

    private LocationData data;

    @Override
    public void onCreate() {
        super.onCreate();

        data = new LocationData();

        startForeground();
    }

    private void startForeground() {
        //BUIDLING A NOTIFICATION

        startForeground(101, notification);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        data = Parcels.unwrap(intent.getParcelableExtra(LOCATION_MESSAGE));

        buildGoogleApiClient();

        mGoogleApiClient.connect();

        if (mGoogleApiClient.isConnected()) {
            startLocationUpdates();
        }

        return START_STICKY;
    }

    protected synchronized void buildGoogleApiClient() {

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();

        createLocationRequest();
    }

    protected void createLocationRequest() {
        mLocationRequest = new LocationRequest();

        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    protected void startLocationUpdates() {

        try {

            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, this);

        } catch (SecurityException ex) {


        } catch (Exception e) {

        }
    }

    private void sendUpdates() {

        data.millisecondTime = SystemClock.uptimeMillis() - data.startTime;
        data.updateTime = data.timeBuff + data.millisecondTime;
        data.seconds = (int) (data.updateTime / 1000);
        data.minutes = data.seconds / 60;
        data.hours = data.minutes / 60;
        data.minutes = data.minutes % 60;
        data.seconds = data.seconds % 60;
        data.milliSeconds = (int) (data.updateTime % 1000);

        Intent locationIntent = new Intent();
        locationIntent.setAction(LOACTION_ACTION);
        locationIntent.putExtra(LOCATION_MESSAGE, Parcels.wrap(data));

        LocalBroadcastManager.getInstance(this).sendBroadcast(locationIntent);
    }

    protected void stopLocationUpdates() {
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
    }

    @Override
    public void onDestroy() {
        stopLocationUpdates();

        mGoogleApiClient.disconnect();

        super.onDestroy();
    }

    @Override
    public void onConnected(Bundle connectionHint) throws SecurityException {
        Log.i(TAG, "Connected to GoogleApiClient");

        if (data.mLastLocation == null) {
            data.mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            sendUpdates();
        }

        startLocationUpdates();

    }

    @Override
    public void onLocationChanged(Location location) {

        sendLocationChanged(location);

        Log.d(TAG, "onLocationChanged: " + location.getLatitude() + ", " + location.getLongitude());

        sendUpdates();

    }

    public void sendLocationChanged(Location location) {
        //SEND DATA TO THE SERVER
    }

    @Override
    public void onConnectionSuspended(int cause) {

        mGoogleApiClient.connect();
    }

    @Override
    public void onConnectionFailed(ConnectionResult result) {

        Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
    }

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

}

After each location update I'm also calculating elapsed time (I have to do this by adding, because user may pause the activity). The problem is when I'm running the app in my office time is exact (for example 35 minutes), but when I'm riding a bicycle time is too short (shows 15mins when actually about 30 minutes passed). For the time perpsective the only difference is that it's called more often (in office gps refreshes only each 10-20 seconds, but outside may be even each 2 seconds). The route and distance are fine - service is not killed or something like that. I'm also updating time in activity, but replacing it on each update with the broadcasted (by the service) time, but even removing this didn't fix the issue. Why is this happening?

Makalele
  • 7,431
  • 5
  • 54
  • 81

2 Answers2

1

As mentioned in the SystemClock documentation,

uptimeMillis() is counted in milliseconds since the system was booted. This clock stops when the system enters deep sleep (CPU off, display dark, device waiting for external input)

So that may be the reason why you get different times. On the other hand, there are elapsedRealtime() and elapsedRealtimeNanos() methods, which

return the time since the system was booted, and include deep sleep. This clock is guaranteed to be monotonic, and continues to tick even when the CPU is in power saving modes, so is the recommend basis for general purpose interval timing

Try using one of them.

Kirill Simonov
  • 8,257
  • 3
  • 18
  • 42
  • 1
    That's it! Using elapsedRealtime() fixed the issue. I never expected updateMillis() to work in such way. – Makalele Dec 22 '17 at 13:58
0

I would suggest using the time you get with each location update in onLocationChanged() and passing it to your sendUpdates() method

void onLocationChanged(Location location){

    long currentTime=location.getTime(); // unix time in milliseconds

    .......

    sendUpdates(currentTime);

}

then in sendUpdates(long currentTime) do the time calculations. If I understand correctly you are interested only in elapsed time since your app started. In that case I guess you can simplify your data object by having just data.startTime, data.seconds, data.minutes and data.hours for the elapsed time. You can obtain the start time from the location of the first fix after the app start (utilizing flag or similar to detect the first fix).

Then in sendUpdates(long currentTime) I would calculate the sec, min, hour of the elapsed time like this:

int elapsedSeconds=int((currentTime-data.StartTime)/1000);
data.hours=int(elapsedSeconds/3600);
data.minutes=int(elapsedSeconds/60)-60*data.hours;
data.seconds=elapsedSeconds-60*(data.minutes+60*data.hours);
colens
  • 467
  • 3
  • 12