s4inz
s4inz

Reputation: 123

Android Service: Good Practice of Activity-Service communication

In my Android application, I'm using a 'LocationService' within a 'Service' class. Several tutorials recommend this practice in order to have a permanent location check across activities. However, despite some good documentation out there, I still have a question concerning the communication between an activity and my service.

This is my LocationService class:

public class LocationService extends Service {

    public static  LocationManager locationManager;
    public static  LocationListener listener;
    private static Location currentLocation = null;

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

     @Override
     public void onStart(Intent intent, int startId) {

        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        listener = new LocationListener() {

           @Override
           public void onLocationChanged(Location location) {
               currentLocation = location;
               System.out.println("Current Location: " + currentLocation);
           }

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

           @Override
           public void onProviderEnabled(String s) {
           }

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

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

    @Override
    public void onDestroy() {
        locationManager.removeUpdates(listener);
    }

    public static Location getCurrentLocation() {
        return currentLocation;
    }

This is part of my Global class:

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

    if(!LocationService.isRunning()) {
        Intent intent = new Intent(this, LocationService.class);
        startService(intent);
    }
}
...

And this is my Activity class:

public class ScreenActivity extends FragmentActivity {

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

    if (usesPosition) {
        Location tLoc = LocationService.getCurrentLocation();
        TextView t = (TextView) findViewById(R.id.tv);
        t.setText(tLoc.toString());
    }
}

I feel like this is not an elegant way, possible even wrong. I do get a good location, but I feel like I don't access the service, but rather treat it like a static class. I'm not yet familiar with the concept of binding services to activities, so I wonder if I really do need to do that.

EDIT 1

Thanks for the first answers! That helped me out a bit already. This is now part of my Activity class:

private ServiceConnection locationServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceDisconnected(ComponentName name) {
        locationServiceBound = false;
        System.out.println("Service disconnected");
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        com.ma.sainz.geoobserver.Tools.Services.LocationService.MyBinder myBinder = (com.ma.sainz.geoobserver.Tools.Services.LocationService.MyBinder) service;
        locationService = myBinder.getService();
        locationServiceBound = true;
        System.out.println("Service bound");
    }
};

where myBinder is a class defined in the LocationService. And that is my new onStart()-Method of my Activity:

@Override
protected void onStart() {
    super.onStart();

    Intent locationServiceIntent = new Intent(this, com.ma.sainz.geoobserver.Tools.Services.LocationService.class);
    bindService(locationServiceIntent, locationServiceConnection, Context.BIND_AUTO_CREATE);
    locationService.requestBackgroundLocation();
    System.out.println("onStart");
}

startService(locationServiceIntent) is done in a previous activity.

However, locationService is null for a while. So Service connection takes some time. How do I know when the Service is connected?

And assuming that I'd like to implement a LocalBroadcast, would that be in addition to the service implementation? Do you have a hint on how to start on that? Because if I'll send locationUpdates as soon as they're there, I wouldn't bother with waiting for the service to connect.

Upvotes: 1

Views: 503

Answers (2)

Vasiliy
Vasiliy

Reputation: 16268

What you have is really not a very good design. Think about this: usually you would like not just get a location, but keep it updated. This means that your Activity should be notified when such updates occur.

You got few options here:

  1. Bind the Service and perform usual method calls on it (get the location, register/unregister listeners)
  2. Use LocalBroadcastManager in order to broadcast location updates from the Service to Activities
  3. Use some event bus (my favorite - GreenRobot's EventBus) in order to broadcast location updates from the Service to Activities

I personally use the latter approach: the Service posts "sticky" events to Event Bus upon location update, and Activities query these events in onResume() and also subscribe for updates. This way your Service won't need to be always running - start it when you need the location, get the location with desired accuracy, and stop the service in order to not consume battery.

Upvotes: 1

Alex Shutov
Alex Shutov

Reputation: 3282

Yes, do not do that - it is not not ellegant, but will also cause memory leak. Android Service can be bound to aby other component, see: https://developer.android.com/guide/components/bound-services.html . Your actvity or another service will bind to your location service and use interface thise ServiceProvides. Notice, service will be running when at least one connection remain. Start and bind to service and then it will run until you stop it and you will be able to use explicit interface from that service by Binder. Another good technic - using EventBus (greenRobot) for passing sticky events between Activity and Service. Event will wait until another component is initialized and receve that event. You can subscribe to that event and unsubscribe from it in onPause and onResume method.

Upvotes: 0

Related Questions