Good practices for services on Android

2020-04-16 19:22发布

问题:

I am currently using 2 services in my app:

1: LocationService, basically trying to localize the user, and aims to stay alive only when the app is on foreground.

2: XmppService, which init the connection with the xmpp server, receive messages, send it, logout ... and aims to stay alive until the user logout.

I've been reading quite a lot of documentation, but I just can't make it clear.

I'm having Leaks when I try to store reference of LocationServiceBinder, which is used to call my service functions (using AIDL interfaces). Same for Xmpp. When I unbind, I get sometimes ANR (which look like to be linked with the fact that my bind/unbind are weirdly done, onResume, onRestart ...).

All the system is working, but I'm sure it is not the right way to do it, and please I would love to follow experienced people to come back in the right side of the force ! :)

Cheers

UPDATE

My Location Service is bind at the app launch to get as fast as possible the user's position :

if(callConnectService == null) {
            callConnectService = new ServiceConnection() {
                public void onServiceConnected(ComponentName name, IBinder binder) {
                    locationServiceBinder = LocationServiceBinder.Stub.asInterface(binder);
                    try {
                        global.setLocationBinder(locationServiceBinder); 
                        global.getLocationBinder().startLocationListener();
                    } catch (Exception e){
                        Log.e(TAG, "Service binder ERROR");
                    }
                }

                public void onServiceDisconnected(ComponentName name) {
                    locationServiceBinder = null;
                }
            };
        }

        /* Launch Service */
        aimConServ =  new Intent(this, LocationService.class);
        boolean bound = bindService(aimConServ,callConnectService,BIND_AUTO_CREATE);

My Xmpp Service is launched when the user log in :

callConnectService = new ServiceConnection() {

            public void onServiceConnected(ComponentName name, IBinder binder) {
                try {
                    Log.d(TAG, "[XMPP_INIT] Complete.");
                    global.setServiceBinder(ConnectionServiceBinder.Stub.asInterface(binder)); 
                    //Connect to XMPP chat
                    global.getServiceBinder().connect();
                } catch (Exception e){
                    Log.e(TAG, "Service binder ERROR ");
                    e.printStackTrace();
                }
            }

            public void onServiceDisconnected(ComponentName name) {
                Log.e(TAG, "Service binder disconnection ");
            }
        };

        /* Launch Service */
        Intent aimConServ =  new Intent(MMWelcomeProfile.this, XmppService.class);
        bound = bindService(aimConServ,callConnectService,Context.BIND_AUTO_CREATE);

and unbind on each Activity :

if (callConnectService != null){
        unbindService(callConnectService);
        callConnectService = null;
    }

回答1:

It hasn't been well-documented in Google's official dev guide, Context.bindService() is actually an asynchronous call. This is the reason why ServiceConnection.onServiceConnected() is used as a callback method, means not happened immediately.

public class MyActivity extends Activity {
  private MyServiceBinder myServiceBinder;

  protected ServiceConnection myServiceConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
      myServiceBinder = (MyServiceBinderImpl) service;
    }

    ... ...
  }

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // bindService() is an asynchronous call. myServiceBinder is resoloved in onServiceConnected()
    bindService(new Intent(this, MyService.class),myServiceConnection, Context.BIND_AUTO_CREATE);
    // You will get a null point reference here, if you try to use MyServiceBinder immediately.
    MyServiceBinder.doSomething(); // <-- not yet resolved so Null point reference here
  }
}

A workaround is call MyServiceBinder.doSomething() in myServiceConnection.onServiceConnected(), or perform MyServiceBinder.doSomething() by some user interaction (e.g. button click), as the lag after you call bindService() and before system get a reference of myServiceBinder is quite soon. as long as you are not using it immediately, you should be just fine.

Check out this SO question CommonsWare's answer for more details.



回答2:

this thread is quite old, but I just discovered it.

Actually there is only one way for your service to go on living if it is bound : it has to be also started. The documentation is not quite clear about that but a service can be both started and bound.

In that case, the service will not get destroyed when you unbind from it, it will get destroyed when :

  • you stop it and there is no one bound to it
  • you unbind from it and it has been stopped before.

I made a small Service Lifecycle demo app on GitHub and it's also available on Google Play.

Hope that helps ;)



回答3:

if you bind to a service in an Activity, you need to unbind it too:

@Override
protected void onResume() {

    Log.d("activity", "onResume");
    if (locationServiceBinder == null) {
        doBindLocationService();
    }
            super.onResume();
}

@Override
protected void onPause() {

    Log.d("activity", "onPause");
    if (locationServiceBinder  != null) {
        unbindService(callConnectService);
        locationServiceBinder = null;
    }
    super.onPause();
}

where doBindLocationService():

public void doBindLocationService() {
    Log.d("doBindService","called");

    aimConServ =  new Intent(this, LocationService.class);
    // Create a new Messenger for the communication back
    // From the Service to the Activity
    bindService(aimConServ, callConnectService, Context.BIND_AUTO_CREATE);
}

You need to do this practise for your XmppService as well