question

Jeffrey Blattman avatar image
Jeffrey Blattman asked Jeffrey Blattman edited

How Do I Use a ServiceConnector from an IntentService?

I want to use a ServiceConnector within my IntentService. How can I do this without leaking service connections?
10 |2000

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

1 Answer

Jeffrey Blattman avatar image
Jeffrey Blattman answered Jeffrey Blattman edited
First consider the lifecycle of an IntentService. Android will create the Service object, call onCreate(), onStartCommand(). It will then call onHandleIntent() some number of times to service all queued tasks. When finished, onDestroy() is called.

Note that we are speaking about the lifecycle of the Android Service, not the lifecycle of the Java Service object. Android makes no assertions about the object's lifecycle. It is therefore prudent to perform any "setup" in onCreate(), teardown in onDestroy(), and no assumptions about the longevity of the Java object.

With this in mind, the recommended pattern for using a ServiceConnector in an IntentService is as follows,

  1. Define an instance field to reference the ServiceConnector instance
  2. In onCreate(), create a new instance of the ServiceConnector. It is not necessary to call connect() on the ServiceConnector after it's created. Doing so will initiate the process of binding to service, but calling any method on the ServiceConnector will implicitly cause a connect. That being said, calling connect() initiates the process of binding to the service so it may increase the chance that you are bound when you make your first ServiceConnector call.
  3. In onHandleIntent(), use the connector. Remember that onHandleIntent() is invoked from a worker (non-main) thread. You should therefore not use the callback versions of the ServiceConnector methods or otherwise create threads (or use an AsyncTask, or Executor) to run the ServiceConnector methods.
  4. In onDestroy(), call disconnect() on the ServiceConnector instance and set it's instance field to null. The purpose of setting the value to null is to ensure that you will never use this instance once you've called disconnect().
For example,

public class TestService extends IntentService {

  private MerchantConnector merchantConnector;

  public TestService() {
    super(TestService.class.getSimpleName());
  }

  @Override
  public void onCreate() {
    super.onCreate();
    merchantConnector = new MerchantConnector(this, CloverAccount.getAccount(this), null);
    merchantConnector.connect();
  }

  @Override
  protected void onHandleIntent(Intent intent) {
    Merchant merchant = merchantConnector.getMerchant();
    // Do some work
  }

  @Override
  public void onDestroy() {
    if (merchantConnector != null) {
      merchantConnector.disconnect();
      merchantConnector = null;
    }
    super.onDestroy();
  }
}

Note that binding to an Android service is not "free" in terms of the resources it consumes. If you expect to make repeat usage of a service connector, starting and restarting and intent service (and hence binding and rebinding) is not the right model. Instead you should consider using a started service that maintains the service connector object and does not call disconnect in between uses.

TODO: Best practice for using a Service Connector from a started service.
10 |2000

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Welcome to the
Clover Developer Community