ArmenHeat
ArmenHeat

Reputation: 111

Broadcast receiver in service still broadcasting after service stopped

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

Answers (2)

Jack Meister
Jack Meister

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

AloDev
AloDev

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

Related Questions