s4inz s4inz - 1 month ago 5
Android Question

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.

Answer

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.