Reputation: 111
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);
}
}
Upvotes: 0
Views: 108
Reputation: 247
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
.)
Upvotes: 1
Reputation: 410
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
Upvotes: 0