0

I'm making an app that tracks a users location and updates them on the distance they traveled, their overall speed and the time they took. When the user taps the StartTracking button in MainActivity.java, MapsActivity.java is started. MapsActivity.java contains the map fragment and the TextViews that display the information. The location tracking is done in a service TrackingService.java so that even if the app is running in the background, location is still being updated.

To send the distance, time and speed, I use a broadcast receiver in the service. Distance is broadcasted whenonLocationChanged()is triggered. To broadcast time and speed I used timer.schedule(). When I tap the StopTracking button in MapsActivity.java, the service is supposed to stop, the activity destroyed and the user should return to the MainActivity.java.

My issue is that when I tap the StopTracking Button, all of the above happens, except the broadcasts in the timer.schedule() still keeps broadcasting. So when I tap StartTracking again in MainActivity, the speed and time TextViews keeps switching between old values and the newer ones. The relevant classes are below, I've removed the redundant code to make them more readable.

Full disclosure: This is for a school assignment. I am to assume perfect GPS conditions and no loss in connectivity. Using BroadCastReciever and Service is required by the assignment.

MapsActivity.java

    public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

    private GoogleMap mMap;
    private Intent intent;
    private TrackingService trackingService;
    private BroadcastReceiver receiver;
    private ArrayList<Location> coordinates;
    private TextView distanceTV, timeTV, speedTV;
    private Button stopTrackingButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);

        coordinates = new ArrayList<Location>();
        distanceTV = (TextView) findViewById(R.id.distanceTextView);
        timeTV = (TextView) findViewById(R.id.timeTextView);
        speedTV = (TextView) findViewById(R.id.speedTextView);

        IntentFilter filter = new IntentFilter();
        filter.addAction("locationChanged");
        filter.addAction("timeChanged");
        filter.addAction("speedChanged");

        stopTrackingListener();

        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {

                if(intent.getAction().equals("locationChanged"))
                {
                    Bundle bundle = intent.getExtras();
                    coordinates = bundle.getParcelableArrayList("COORDINATES");

                    float distance = bundle.getFloat("DISTANCE");
                    distanceTV.setText("Distance: " + Float.toString((float) Math.round(distance *100f) / 100f) + "m");
                    Log.d("test", "onReceive location");
                    redrawLine();
                }
                else if (intent.getAction().equals("timeChanged"))
                {
                    Bundle bundle = intent.getExtras();
                    long time = bundle.getLong("TIME");

                    float hours = (float) (((time/1000) / 60) / 60);
                    float mins = (float) (((time/1000) / 60) % 60);
                    float secs = (float) ((time/1000) %60);

                    timeTV.setText(String.format("Time: %.0fh: %.0fm: %.0fs", hours, mins, secs));
                }
                else if (intent.getAction().equals("speedChanged"))
                {
                    Bundle bundle = intent.getExtras();
                    float speed = bundle.getFloat("SPEED");
                    speedTV.setText("Avg Speed: " + Float.toString (Math.round(speed *100f) / 100f) + "m/s");
                }
            }
        };
        LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter);
    }


    private ServiceConnection trackerConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            TrackingService.TrackingBinder binder = (TrackingService.TrackingBinder)iBinder;
            trackingService = binder.getService();

            Log.d("test", "Service is Connected");
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.d("test", "Service Disconnected");
        }
    };

    //Start the service as soon as activity starts
    @Override
    protected void onStart()
    {
        super.onStart();
        if(intent == null)
        {
            intent = new Intent(getBaseContext(), TrackingService.class);
            bindService(intent, trackerConnection, Context.BIND_AUTO_CREATE);
            startService(intent);
            Log.d("test", "Service Started");
        }
    }

    //Unbind and destroy service when app is destroyed
    @Override
    protected void onDestroy()
    {
        unbindService(trackerConnection);
        getBaseContext().stopService(intent);
        Log.d("test", "Service Destroyed");
        super.onDestroy();
    }

    //Listens for user tapping on "Stop tracking..." button.
    public void stopTrackingListener()
    {
        stopTrackingButton = (Button)findViewById(R.id.stopTrackingButton);

        stopTrackingButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {

                Log.d("test", "Stopped tracking...");
                getBaseContext().stopService(intent);
                trackingService = null;

                Intent i =new Intent (MapsActivity.this, MainActivity.class);
                startActivity(i);

                finish();
            }
        });
    }

}

TrackingService.java

    public class TrackingService extends Service implements LocationListener{

    private final IBinder trackingBind = new TrackingBinder();
    private ArrayList<Location> points;
    LocationManager locationManager;
    StopWatch timer;


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

        points = new ArrayList<Location>();
        timer = new StopWatch();

        locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        requestLocation();
        updateTimeAndSpeed();
    }

    private void requestLocation() {

        if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }

        locationManager.requestLocationUpdates(locationManager.GPS_PROVIDER, 1000, 1, this);
        timer.start();
    }

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

    @Override
    public boolean onUnbind(Intent intent)
    {
        timer.stop();
        Log.d("test2", "Service onUnbind");
        return false;
    }

    @Override
    public void onLocationChanged(Location location) {

        points.add(location);
        broadcastCurrentLocation();
    }


    public class TrackingBinder extends Binder
    {
        TrackingService getService()
        {
            return TrackingService.this;
        }
    }

    public void updateTimeAndSpeed()
    {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                broadcastElapsedTime();
                broadcastAverageSpeed();
                Log.d("test2", "Broadcasting...");
            }
        }, 0, 1000);
    }

    public void broadcastElapsedTime()
    {
        Intent intent = new Intent ("timeChanged");
        intent.putExtra("TIME", getTime());
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }

    public void broadcastCurrentLocation()
    {
        Intent intent = new Intent ("locationChanged");
        intent.putParcelableArrayListExtra("COORDINATES", points);
        intent.putExtra("DISTANCE", calculateDistance());
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }

    public void broadcastAverageSpeed()
    {
        Intent intent = new Intent ("speedChanged");
        intent.putExtra("SPEED", getSpeed());
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }

    public float calculateDistance()
    {
        float distance = 0;

        if(null != points && (!points.isEmpty()))
        {
            for(int i = 1; i < points.size(); i++)
            {
                Location currLoc = new Location(points.get(i));
                Location lastLoc = new Location(points.get(i-1));

                distance += lastLoc.distanceTo(currLoc);
            }
        }
        return distance;
    }

    public long getTime()
    {
        return timer.getElapsedTime();
    }


    public float getSpeed()
    {
        float distance = calculateDistance();

        long time = getTime();
        float timeAsSeconds = (float) (time/1000);

        return (distance / timeAsSeconds);
    }


}
Apurva Kolapkar
  • 1,270
  • 2
  • 16
  • 32
ArmenHeat
  • 111
  • 8
  • I think adding some flag for checking service was alive or stop may help. – anhtuannd Dec 27 '17 at 07:52
  • @anhtuannd In MapsActivity, I used [this](https://stackoverflow.com/a/5921190/7859365) and called `isMyServiceRunning(TrackingService.class)` in a timer.schedule task to post the boolean in logcat every second When the service starts, it continuously shows true in logcat. When I tap the stopTrackingButton, it starts posting false. So my assumption is that the service does stop properly. – ArmenHeat Dec 27 '17 at 08:19
  • 1
    Have you checked this? https://stackoverflow.com/questions/4470418/stopservice-doesnt-stops-my-service-why – anhtuannd Dec 27 '17 at 09:17
  • @anhtuannd, ahh [Jack's Answer](https://stackoverflow.com/a/47989023/7859365) gives the same solution. Using the code I mentioned earlier, I checked if the service was actually stopping and since it was, I didn't look into it further and started trying to fix it myself... which didn't work and that's why I came here. – ArmenHeat Dec 27 '17 at 09:32

2 Answers2

1

Override onDestroy() in your Service subclass. Inside that method, you can call Timer.cancel() on your fixed-delay timer. (You'll need to keep a reference to that timer as a field within TrackingService.java.)

Jack Meister
  • 247
  • 3
  • 6
0

I think the problem is that you don't call locationManager.removeUpdates when using locationManager.requestLocationUpdates. So try to add

locationManager.removeUpdates(this);

to your onUnbind method of TrackingService class

AloDev
  • 410
  • 3
  • 13